Browse Source

组件优化

main
likunming 2 months ago
parent
commit
7318f2be95
  1. 38
      io.sc.platform.core.frontend/src/platform/components/form/Field.vue
  2. 35
      io.sc.platform.core.frontend/src/platform/components/form/FormElement.vue
  3. 124
      io.sc.platform.core.frontend/src/platform/components/form/FormGroup.vue
  4. 376
      io.sc.platform.core.frontend/src/platform/components/form/WForm.vue
  5. 155
      io.sc.platform.core.frontend/src/platform/components/form/WForm_bak.vue
  6. 389
      io.sc.platform.core.frontend/src/platform/components/form/elements/WCodeMirror.vue
  7. 218
      io.sc.platform.core.frontend/src/platform/components/form/elements/WCron.vue
  8. 76
      io.sc.platform.core.frontend/src/platform/components/form/elements/w-code-mirror/PlaceholderPlugin.ts
  9. 251
      io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/DaySegment.vue
  10. 185
      io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/HourSegment.vue
  11. 185
      io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/MinuteSegment.vue
  12. 197
      io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/MonthSegment.vue
  13. 185
      io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/SecondSegment.vue
  14. 233
      io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/WeekSegment.vue
  15. 93
      io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/YearSegment.vue
  16. 5
      io.sc.platform.core.frontend/src/platform/components/form/elements/w-query-condition/WExpression.vue
  17. 47
      io.sc.platform.core.frontend/src/platform/components/form/ts/ComputedManager.ts
  18. 14
      io.sc.platform.core.frontend/src/platform/components/form/ts/Constant.ts
  19. 189
      io.sc.platform.core.frontend/src/platform/components/form/ts/ExposeApiManager.ts
  20. 229
      io.sc.platform.core.frontend/src/platform/components/form/ts/Form.ts
  21. 10
      io.sc.platform.core.frontend/src/platform/components/form/ts/types/ComponentFunctionsType.ts
  22. 4
      io.sc.platform.core.frontend/src/platform/components/form/ts/types/ErrorType.ts
  23. 22
      io.sc.platform.core.frontend/src/platform/components/form/ts/types/PropsType.ts
  24. 2
      io.sc.platform.core.frontend/src/platform/components/grid/Td.vue
  25. 21
      io.sc.platform.core.frontend/src/platform/components/grid/extra/config/ConfigPanel.vue
  26. 2
      io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/AloneGroup.vue
  27. 2
      io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/CheckboxSelection.vue
  28. 2
      io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/Dense.vue
  29. 2
      io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/DisplayColumn.vue
  30. 2
      io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/Fullscreen.vue
  31. 2
      io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/Separator.vue
  32. 2
      io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/SortNo.vue
  33. 2
      io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/StickyColumn.vue
  34. 39
      io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/Stripe.vue
  35. 2
      io.sc.platform.core.frontend/src/platform/components/grid/ts/GridTools.ts
  36. 27
      io.sc.platform.core.frontend/src/platform/components/grid/ts/function/Operator.ts
  37. 8
      io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/ConfigPanelStoreType.ts
  38. 367
      io.sc.platform.core.frontend/src/views/likm/Form.vue
  39. 273
      io.sc.platform.core.frontend/src/views/likm/Grid.vue
  40. 125
      io.sc.platform.core.frontend/src/views/likm/SelectMenuTreeGrid.vue
  41. 66
      io.sc.platform.core.frontend/src/views/likm/SelectUserDialog.vue
  42. 165
      io.sc.platform.core.frontend/src/views/likm/SelectUserGrid.vue
  43. 1
      io.sc.platform.core.frontend/src/views/likm/TreeGrid.vue
  44. 170
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/service/impl/OrgSearchServiceImpl.java
  45. 201
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/service/impl/UserSearchServiceImpl.java
  46. 48
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/support/CriteriaHandler.java
  47. 18
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/support/SearchConstant.java
  48. 46
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/support/UserSearchQueryParameter.java

38
io.sc.platform.core.frontend/src/platform/components/form/Field.vue

@ -0,0 +1,38 @@
<template>
<template v-if="field.name">
<component
:is="field.type"
:ref="(el) => form.setComponentRef(el, field.name)"
v-model="form.data[field.name]"
v-bind="field"
:form="form.instance"
:style="form.getFieldStyle(field)"
@update:model-value="form.cf.updateModelValue(field.name, form.data[field.name])"
></component>
</template>
<template v-else>
<component
:is="field.type"
:ref="(el) => form.setComponentRef(el, field.name)"
v-bind="field"
:form="form.instance"
:style="form.getFieldStyle(field)"
></component>
</template>
</template>
<script setup lang="ts">
import { inject } from 'vue';
import { Form } from './ts/Form';
const form = <Form>inject('form');
const props = defineProps({
field: {
type: Object,
default: () => {
return {};
},
},
});
</script>

35
io.sc.platform.core.frontend/src/platform/components/form/FormElement.vue

@ -0,0 +1,35 @@
<template>
<template v-if="field.type === Constant.GROUP_TYPE">
<FormGroup v-if="showFormGroupComputed" v-bind="field" :fields="field.fields" :style="form.getFieldStyle(field)"></FormGroup>
</template>
<template v-else>
<Field :field="props.field"></Field>
</template>
</template>
<script setup lang="ts">
import { inject, computed } from 'vue';
import { Form } from './ts/Form';
import { Constant } from './ts/Constant';
import Field from './Field.vue';
import FormGroup from './FormGroup.vue';
const form = <Form>inject('form');
const props = defineProps({
field: {
type: Object,
default: () => {
return {};
},
},
});
const showFormGroupComputed = computed(() => {
if (props.field.fields && Array.isArray(props.field.fields) && props.field.fields.length > 0) {
return true;
} else {
return false;
}
});
</script>

124
io.sc.platform.core.frontend/src/platform/components/form/FormGroup.vue

@ -0,0 +1,124 @@
<template>
<div>
<q-card flat bordered>
<q-item dense style="padding: 0px">
<q-item-section>
<q-item-label header :style="headerStyleComputed">
<q-icon v-if="props.icon" :name="props.icon" size="sm" v-bind="props.iconAttrs" />
<span style="margin-left: 5px">{{ label }}</span>
</q-item-label>
</q-item-section>
</q-item>
<q-separator />
<q-card-section style="padding: 8px">
<div class="grid" :style="layoutStyleComputed">
<template v-for="(field, index) in fields as any" :key="String(index)">
<FormElement :field="field"></FormElement>
</template>
</div>
</q-card-section>
</q-card>
</div>
</template>
<script setup lang="ts">
import { inject, computed } from 'vue';
import { useQuasar } from 'quasar';
import { Form } from './ts/Form';
import FormElement from './FormElement.vue';
const $q = useQuasar();
const form = <Form>inject('form');
//
const colorOpacity = 0.048;
const colors = [
//
'rgba(255, 0, 0, ' + colorOpacity + ')',
//
'rgba(255, 149, 0, ' + colorOpacity + ')',
//
'rgba(255, 255, 0, ' + colorOpacity + ')',
// 绿
'rgba(4, 255, 0, ' + colorOpacity + ')',
//
'rgba(0, 255, 255, ' + colorOpacity + ')',
//
'rgba(0, 38, 255, ' + colorOpacity + ')',
//
'rgba(255, 0, 247, ' + colorOpacity + ')',
//
'rgba(0, 0, 0, ' + colorOpacity + ')',
];
const props = defineProps({
//
label: { type: String, default: '' },
//
color: { type: String, default: undefined },
//
icon: { type: String, default: undefined },
//
iconAttrs: { type: Object, default: undefined },
// form
colsNum: { type: [Number, Object], default: 0 },
//
fields: {
type: Array,
default: () => {
return [];
},
},
});
const headerStyleComputed = computed(() => {
const style = {
//
display: 'flex',
alignItems: 'center',
//
color: '#000',
padding: '5px',
};
if (props.color) {
//
if (props.color === 'auto') {
const index = form.fieldArray.value.findIndex((item) => item.label === props.label);
let colorIndex = index > -1 && index < colors.length ? index : getRandomInt();
style['background-color'] = colors[colorIndex];
} else {
style['background-color'] = props.color;
}
}
return style;
});
//
const getRandomInt = () => {
const min = 0;
const max = colors.length - 1;
return Math.floor(Math.random() * (max - min + 1) + min);
};
const screenColsNumComputed = computed(() => {
if (typeof props.colsNum === 'number' && props.colsNum > 0) {
return props.colsNum;
} else if (typeof props.colsNum === 'object') {
const screen = { ...form.cm.screenCols, ...props.colsNum };
return screen[$q.screen.name];
}
return form.cm.screenCols[$q.screen.name];
});
const layoutStyleComputed = computed(() => {
const style = {};
if (typeof props.colsNum === 'number' && props.colsNum > 0) {
style['grid-template-columns'] = 'repeat(' + props.colsNum + ', minmax(0, 1fr))';
} else {
style['grid-template-columns'] = 'repeat(' + screenColsNumComputed.value + ', minmax(0, 1fr))';
}
style['column-gap'] = form.props.xGap + 'px';
style['row-gap'] = form.props.yGap + 'px';
return style;
});
</script>

376
io.sc.platform.core.frontend/src/platform/components/form/WForm.vue

@ -1,28 +1,9 @@
<template> <template>
<div> <div>
<q-form ref="formRef" :autofocus="false" :greedy="true" v-bind="attrs"> <q-form ref="formRef" :autofocus="false" :greedy="true" v-bind="attrs">
<div v-if="fieldsComputed.length > 0" class="grid" :style="formLayoutStyleComputed"> <div v-if="form.fieldArray.value.length > 0" class="grid" :style="form.cm.formLayoutStyleComputed.value">
<template v-for="(field, index) in fieldsComputed as any" :key="String(index)"> <template v-for="(field, index) in form.fieldArray.value as any" :key="String(index)">
<template v-if="field.name"> <FormElement :field="field"></FormElement>
<component
:is="field.type"
:ref="(el) => setComponentRef(el, field.name)"
v-model="formData[field.name]"
v-bind="field"
:form="instance"
:style="fieldStyleComputed(field)"
@update:model-value="updateModelValue(field.name, formData[field.name])"
></component>
</template>
<template v-else>
<component
:is="field.type"
:ref="(el) => setComponentRef(el, field.name)"
v-bind="field"
:form="instance"
:style="fieldStyleComputed(field)"
></component>
</template>
</template> </template>
</div> </div>
<slot></slot> <slot></slot>
@ -31,15 +12,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, watch, computed, toRaw, useAttrs, getCurrentInstance } from 'vue'; import { getCurrentInstance, provide, ref, useAttrs, toRaw, watch } from 'vue';
import { useQuasar } from 'quasar'; import { VueTools } from '@/platform';
import { VueTools, Tools, $t } from '@/platform'; import { Form } from './ts/Form';
import { PageStatusEnum } from '@/platform/components/utils'; import FormElement from './FormElement.vue';
import { getDefaultValue } from './FormField.ts';
const $q = useQuasar();
const attrs = useAttrs(); const attrs = useAttrs();
const componentRef = ref({}); const formRef = ref();
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -49,8 +28,8 @@ const props = defineProps({
}, },
}, },
colsNum: { type: [Number, Object], default: 0 }, colsNum: { type: [Number, Object], default: 0 },
colsXGap: { type: Number, default: 8 }, xGap: { type: Number, default: 8 },
colsYGap: { type: Number, default: 4 }, yGap: { type: Number, default: 4 },
fields: { fields: {
type: Array, type: Array,
default: () => { default: () => {
@ -58,310 +37,75 @@ const props = defineProps({
}, },
}, },
}); });
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'updateValue', args: object): void; (e: 'updateValue', args: object): void;
}>(); }>();
const getFormRef = () => {
return formRef.value;
};
const updateModelValue = (name: string, value: any) => {
if (
(form.fields[name] && (form.fields[name]['type'] !== 'w-number' || Number.isFinite(value)) && form.fields[name]['type'] !== 'w-integer') ||
Number.isFinite(value)
) {
if (form.fields[name].error) {
form.fields[name].error = false;
form.fields[name].errorMessage = undefined;
}
emit('updateValue', {
form: instance,
value: toRaw(form.data),
fieldName: name,
fieldValue: value,
});
}
};
const form = new Form(props);
form.cf = {
getFormRef,
updateModelValue,
};
const localFlag = ref(false); /**
const formRef = ref(); * 字段集合监听当字段长度发生变化重新构建 fields 数据模型
const formStatus = ref(PageStatusEnum.新增); */
const formModel: any = {};
const formFields = {};
let fields_ = ref([...props.fields]);
// colsNum 0
const screenCols = { xs: 1, sm: 2, md: 3, lg: 4, xl: 6 };
watch( watch(
() => props.fields, () => props.fields,
(newVal, oldVal) => { (newVal, oldVal) => {
if (newVal.length !== oldVal.length) { if (newVal.length !== oldVal.length) {
fields_ = ref([...props.fields]); form.buildFieldsAndData();
for (const field of fields_.value as any) {
if (field.name) {
formModel[field.name] = getDefaultValue(field);
formFields[field.name] = field;
}
}
} }
}, },
); );
/**
watch( * 字段label监听当字段label发生变化重新构建 fields
() => props.fields.map((item) => item['label']), */
(newVal, oldVal) => {
fields_ = ref([...props.fields]);
for (const field of fields_.value as any) {
if (field.name) {
formFields[field.name] = field;
}
}
localFlag.value = !localFlag.value;
},
);
const fieldsComputed = computed(() => {
if (!Tools.isEmpty(localFlag.value)) {
return fields_.value;
}
return [];
});
for (const field of fields_.value as any) {
if (field.name) {
formModel[field.name] = getDefaultValue(field);
formFields[field.name] = field;
}
}
let formData = reactive(!Tools.isEmpty(props.modelValue) ? props.modelValue : formModel);
watch( watch(
() => props.modelValue, () => props.fields.map((item: any) => item['label']),
(newVal, oldVal) => { (newVal, oldVal) => {
formData = reactive(!Tools.isEmpty(props.modelValue) ? props.modelValue : formModel); form.buildFields();
}, },
); );
const screenColsNumComputed = computed(() => {
if (typeof props.colsNum === 'number' && props.colsNum > 0) {
return props.colsNum;
} else if (typeof props.colsNum === 'object') {
const screen = { ...screenCols, ...props.colsNum };
return screen[$q.screen.name];
}
return screenCols[$q.screen.name];
});
const formLayoutClassComputed = computed(() => {
let className = '';
if (typeof props.colsNum === 'number' && props.colsNum > 0) {
className = 'grid-cols-' + props.colsNum;
} else {
className = 'grid-cols-' + screenColsNumComputed.value;
}
className += ' gap-x-[' + props.colsXGap + 'px]';
className += ' gap-y-[' + props.colsYGap + 'px]';
return className;
});
/**
* 采用 style 替换动态 tailwindcss class
*/
const formLayoutStyleComputed = computed(() => {
const style = {};
if (typeof props.colsNum === 'number' && props.colsNum > 0) {
style['grid-template-columns'] = 'repeat(' + props.colsNum + ', minmax(0, 1fr))';
} else {
style['grid-template-columns'] = 'repeat(' + screenColsNumComputed.value + ', minmax(0, 1fr))';
}
style['column-gap'] = props.colsXGap + 'px';
style['row-gap'] = props.colsYGap + 'px';
return style;
});
/**
* 采用 style 替换动态 tailwindcss class
*/
const fieldStyleComputed = (field) => {
const style = {};
if (field) {
if (field.firstCol) {
style['grid-column-start'] = 1;
if (field.colSpan === 'full' || (typeof field.colSpan === 'number' && screenColsNumComputed.value < field.colSpan)) {
style['grid-column-end'] = `${screenColsNumComputed.value + 1}`;
} else if (typeof field.colSpan === 'number' && field.colSpan > 0) {
style['grid-column-end'] = `${field.colSpan + 1}`;
}
} else {
if (field.colSpan === 'full') {
// col-span-${screenColsNumComputed.value}
style['grid-column'] = `span ${screenColsNumComputed.value} / span ${screenColsNumComputed.value}`;
} else {
if (field.colSpan && screenColsNumComputed.value >= field.colSpan) {
// col-span-${field.colSpan}
style['grid-column'] = `span ${field.colSpan} / span ${field.colSpan}`;
} else {
// col-span-1
style['grid-column'] = 'span 1 / span 1';
}
}
}
}
return style;
};
const updateModelValue = (name, value) => {
if (formFields[name].error) {
formFields[name].error = false;
formFields[name].errorMessage = undefined;
}
emit('updateValue', {
form: instance,
value: toRaw(formData),
fieldName: name,
fieldValue: value,
});
};
const setComponentRef = (el, fieldName) => {
if (el) {
componentRef.value[fieldName] = el;
}
};
/**
* 对外暴露方法-获取form所有数据
*/
const getData = () => {
const data = { ...toRaw(formData) };
return data;
};
/**
* 对外暴露方法-设置form所有字段值
* @param data 数据对象(JSON格式)
*/
const setData = (data) => {
if (Tools.isEmpty(data)) {
for (const field of fields_.value as any) {
formData[field.name] = getDefaultValue(field);
}
} else {
for (const field of fields_.value as any) {
if (data[field.name] !== undefined) {
formData[field.name] = data[field.name];
}
}
}
};
/**
* 对外暴露方法-重置表单
*/
const reset = () => {
Object.keys(formData).forEach((key) => {
formData[key] = getDefaultValue(formFields[key]);
});
};
const formValidate = async () => {
let validate = false;
await formRef.value.validate().then((success) => {
if (success) {
validate = true;
}
});
return validate;
};
/**
* 对外暴露方法-表单验证
*/
const validate = async () => {
const v = await formValidate();
return v;
};
/**
* 对外暴露方法-设置字段值
* @param fieldName 字段name
* @param value 字段值
*/
const setFieldValue = (fieldName, value) => {
formData[fieldName] = value;
};
/**
* 对外暴露方法-获取字段值
* @param fieldName 字段name
*/
const getFieldValue = (fieldName) => {
return formData[fieldName];
};
/**
* 对外暴露方法-设置form状态
* @param status 状态
*/
const setStatus = (status) => {
formStatus.value = status;
};
/**
* 对外暴露方法-获取form状态
*/
const getStatus = () => {
return toRaw(formStatus.value);
};
/**
* 对外暴露方法-获取当前一行应该显示的元素个数
*/
const getColsNum = () => {
return screenColsNumComputed.value;
};
/**
* 错误消息类型
*
* @param fieldName 字段名
* @param errorMessage 错误消息
*/
type errorType = {
fieldName: string;
errorMessage: string;
};
/**
* 对外暴露方法-设置后台校验错误信息
* @param errors 错误消息集合
*/
const setValidationErrors = (errors: errorType[]) => {
if (errors && errors.length > 0) {
const grouped = {};
errors.map(({ fieldName, ...rest }) => {
grouped[fieldName] = grouped[fieldName] || [];
grouped[fieldName].push(rest);
});
for (let name in grouped) {
if (formFields[name]) {
formFields[name].error = true;
formFields[name].errorMessage = grouped[name]
.map((obj) => {
if (!Tools.isEmpty(obj['errorMessageI18nKey'])) {
return $t(obj['errorMessageI18nKey']);
}
return obj.errorMessage;
})
.join('、');
}
}
}
};
const getFields = () => {
return formFields;
};
const getFieldComponent = (name) => {
return componentRef.value[name];
};
const resetValidation = () => {
formRef.value.resetValidation();
Object.keys(formFields).forEach((field) => {
if (Tools.hasOwnProperty(formFields[field], 'error') && formFields[field]['error']) {
formFields[field]['error'] = false;
}
});
};
defineExpose({ defineExpose({
getFields, getData: form.api.getData,
getData, setData: form.api.setData,
setData, getStatus: form.api.getStatus,
reset, setStatus: form.api.setStatus,
validate, reset: form.api.reset,
getFieldValue, validate: form.api.validate,
setFieldValue, setFieldValue: form.api.setFieldValue,
setStatus, getFieldValue: form.api.getFieldValue,
getStatus, getColsNum: form.api.getColsNum,
getColsNum, setValidationErrors: form.api.setValidationErrors,
setValidationErrors, getFieldComponent: form.api.getFieldComponent,
getFieldComponent, resetValidation: form.api.resetValidation,
resetValidation, getFields: form.api.getFields,
}); });
const instance = getCurrentInstance(); const instance = getCurrentInstance();
VueTools.expose2Instance(instance); VueTools.expose2Instance(instance);
form.setInstance(instance);
provide('form', form);
</script> </script>

155
io.sc.platform.core.frontend/src/platform/components/form/WForm_bak.vue

@ -1,23 +1,19 @@
<template> <template>
<div> <div>
<q-form ref="formRef" :autofocus="false" :greedy="true" v-bind="attrs"> <q-form ref="formRef" :autofocus="false" :greedy="true" v-bind="attrs">
<div v-if="fieldsComputed.length > 0" class="grid" :class="formLayoutComputed"> <div v-if="fieldsComputed.length > 0" class="grid" :style="formLayoutStyleComputed">
<template v-for="(field, index) in fieldsComputed as any" :key="String(index)"> <template v-for="(field, index) in fieldsComputed as any" :key="String(index)">
<template v-if="field.name"> <template v-if="field.type === 'w-form-group'">
<WFormGroup v-bind="field" :style="fieldStyleComputed(field)"></WFormGroup>
</template>
<template v-else-if="field.name">
<component <component
:is="field.type" :is="field.type"
:ref="(el) => setComponentRef(el, field.name)" :ref="(el) => setComponentRef(el, field.name)"
v-model="formData[field.name]" v-model="formData[field.name]"
v-bind="field" v-bind="field"
:form="instance" :form="instance"
:class=" :style="fieldStyleComputed(field)"
(field.colsFirst ? 'col-start-1 ' : ' ') +
(field.colSpan === 'full'
? ' col-span-' + screenColsNumComputed
: field.colSpan && screenColsNumComputed >= field.colSpan
? ' col-span-' + field.colSpan
: ' col-span-1')
"
@update:model-value="updateModelValue(field.name, formData[field.name])" @update:model-value="updateModelValue(field.name, formData[field.name])"
></component> ></component>
</template> </template>
@ -25,17 +21,9 @@
<component <component
:is="field.type" :is="field.type"
:ref="(el) => setComponentRef(el, field.name)" :ref="(el) => setComponentRef(el, field.name)"
:form-ref="formRef"
v-bind="field" v-bind="field"
:form="instance" :form="instance"
:class=" :style="fieldStyleComputed(field)"
(field.colsFirst ? 'col-start-1 ' : ' ') +
(field.colSpan === 'full'
? ' col-span-' + screenColsNumComputed
: field.colSpan && screenColsNumComputed >= field.colSpan
? ' col-span-' + field.colSpan
: ' col-span-1')
"
></component> ></component>
</template> </template>
</template> </template>
@ -46,10 +34,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, watch, computed, toRaw, useAttrs, getCurrentInstance } from 'vue'; import { ref, reactive, watch, computed, toRaw, useAttrs, getCurrentInstance, provide } from 'vue';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { VueTools, Tools } from '@/platform'; import { VueTools, Tools, $t } from '@/platform';
import { PageStatusEnum } from '@/platform/components/utils'; import { PageStatusEnum } from '@/platform/components/utils';
import { getDefaultValue } from './FormField.ts';
import WFormGroup from '@/platform/components/form-group/WFormGroup.vue';
const $q = useQuasar(); const $q = useQuasar();
const attrs = useAttrs(); const attrs = useAttrs();
@ -73,6 +63,10 @@ const props = defineProps({
}, },
}); });
const emit = defineEmits<{
(e: 'updateValue', args: object): void;
}>();
const localFlag = ref(false); const localFlag = ref(false);
const formRef = ref(); const formRef = ref();
const formStatus = ref(PageStatusEnum.新增); const formStatus = ref(PageStatusEnum.新增);
@ -82,23 +76,6 @@ let fields_ = ref([...props.fields]);
// colsNum 0 // colsNum 0
const screenCols = { xs: 1, sm: 2, md: 3, lg: 4, xl: 6 }; const screenCols = { xs: 1, sm: 2, md: 3, lg: 4, xl: 6 };
const defaultValueHandler = (field) => {
if (!Tools.isUndefinedOrNull(field.defaultValue)) {
return field.defaultValue;
} else if (field.type === 'w-checkbox') {
return false;
} else if (field.type === 'w-option-group') {
if (!field.optionType || field.optionType === 'radio') {
return undefined;
} else {
return [];
}
} else if (field.type === 'w-select' && field.multiple) {
return [];
}
return undefined;
};
watch( watch(
() => props.fields, () => props.fields,
(newVal, oldVal) => { (newVal, oldVal) => {
@ -106,7 +83,7 @@ watch(
fields_ = ref([...props.fields]); fields_ = ref([...props.fields]);
for (const field of fields_.value as any) { for (const field of fields_.value as any) {
if (field.name) { if (field.name) {
formModel[field.name] = defaultValueHandler(field); formModel[field.name] = getDefaultValue(field);
formFields[field.name] = field; formFields[field.name] = field;
} }
} }
@ -127,13 +104,15 @@ watch(
}, },
); );
const fieldsComputed = computed(() => { const fieldsComputed = computed(() => {
localFlag.value; if (!Tools.isEmpty(localFlag.value)) {
return fields_.value; return fields_.value;
}
return [];
}); });
for (const field of fields_.value as any) { for (const field of fields_.value as any) {
if (field.name) { if (field.name) {
formModel[field.name] = defaultValueHandler(field); formModel[field.name] = getDefaultValue(field);
formFields[field.name] = field; formFields[field.name] = field;
} }
} }
@ -155,7 +134,8 @@ const screenColsNumComputed = computed(() => {
} }
return screenCols[$q.screen.name]; return screenCols[$q.screen.name];
}); });
const formLayoutComputed = computed(() => {
const formLayoutClassComputed = computed(() => {
let className = ''; let className = '';
if (typeof props.colsNum === 'number' && props.colsNum > 0) { if (typeof props.colsNum === 'number' && props.colsNum > 0) {
className = 'grid-cols-' + props.colsNum; className = 'grid-cols-' + props.colsNum;
@ -167,11 +147,63 @@ const formLayoutComputed = computed(() => {
return className; return className;
}); });
/**
* 采用 style 替换动态 tailwindcss class
*/
const formLayoutStyleComputed = computed(() => {
const style = {};
if (typeof props.colsNum === 'number' && props.colsNum > 0) {
style['grid-template-columns'] = 'repeat(' + props.colsNum + ', minmax(0, 1fr))';
} else {
style['grid-template-columns'] = 'repeat(' + screenColsNumComputed.value + ', minmax(0, 1fr))';
}
style['column-gap'] = props.colsXGap + 'px';
style['row-gap'] = props.colsYGap + 'px';
return style;
});
/**
* 采用 style 替换动态 tailwindcss class
*/
const fieldStyleComputed = (field) => {
const style = {};
if (field) {
if (field.firstCol) {
style['grid-column-start'] = 1;
if (field.colSpan === 'full' || (typeof field.colSpan === 'number' && screenColsNumComputed.value < field.colSpan)) {
style['grid-column-end'] = `${screenColsNumComputed.value + 1}`;
} else if (typeof field.colSpan === 'number' && field.colSpan > 0) {
style['grid-column-end'] = `${field.colSpan + 1}`;
}
} else {
if (field.colSpan === 'full') {
// col-span-${screenColsNumComputed.value}
style['grid-column'] = `span ${screenColsNumComputed.value} / span ${screenColsNumComputed.value}`;
} else {
if (field.colSpan && screenColsNumComputed.value >= field.colSpan) {
// col-span-${field.colSpan}
style['grid-column'] = `span ${field.colSpan} / span ${field.colSpan}`;
} else {
// col-span-1
style['grid-column'] = 'span 1 / span 1';
}
}
}
}
return style;
};
const updateModelValue = (name, value) => { const updateModelValue = (name, value) => {
if (formFields[name].error) { if (formFields[name].error) {
formFields[name].error = false; formFields[name].error = false;
formFields[name].errorMessage = undefined; formFields[name].errorMessage = undefined;
} }
emit('updateValue', {
form: instance,
value: toRaw(formData),
fieldName: name,
fieldValue: value,
});
}; };
const setComponentRef = (el, fieldName) => { const setComponentRef = (el, fieldName) => {
@ -192,9 +224,15 @@ const getData = () => {
* @param data 数据对象(JSON格式) * @param data 数据对象(JSON格式)
*/ */
const setData = (data) => { const setData = (data) => {
for (const field of fields_.value as any) { if (Tools.isEmpty(data)) {
if (data[field.name] !== undefined) { for (const field of fields_.value as any) {
formData[field.name] = data[field.name]; formData[field.name] = getDefaultValue(field);
}
} else {
for (const field of fields_.value as any) {
if (data[field.name] !== undefined) {
formData[field.name] = data[field.name];
}
} }
} }
}; };
@ -203,7 +241,7 @@ const setData = (data) => {
*/ */
const reset = () => { const reset = () => {
Object.keys(formData).forEach((key) => { Object.keys(formData).forEach((key) => {
formData[key] = defaultValueHandler(formFields[key]); formData[key] = getDefaultValue(formFields[key]);
}); });
}; };
const formValidate = async () => { const formValidate = async () => {
@ -284,6 +322,9 @@ const setValidationErrors = (errors: errorType[]) => {
formFields[name].error = true; formFields[name].error = true;
formFields[name].errorMessage = grouped[name] formFields[name].errorMessage = grouped[name]
.map((obj) => { .map((obj) => {
if (!Tools.isEmpty(obj['errorMessageI18nKey'])) {
return $t(obj['errorMessageI18nKey']);
}
return obj.errorMessage; return obj.errorMessage;
}) })
.join('、'); .join('、');
@ -300,6 +341,15 @@ const getFieldComponent = (name) => {
return componentRef.value[name]; return componentRef.value[name];
}; };
const resetValidation = () => {
formRef.value.resetValidation();
Object.keys(formFields).forEach((field) => {
if (Tools.hasOwnProperty(formFields[field], 'error') && formFields[field]['error']) {
formFields[field]['error'] = false;
}
});
};
defineExpose({ defineExpose({
getFields, getFields,
getData, getData,
@ -313,8 +363,19 @@ defineExpose({
getColsNum, getColsNum,
setValidationErrors, setValidationErrors,
getFieldComponent, getFieldComponent,
resetValidation,
}); });
const instance = getCurrentInstance(); const instance = getCurrentInstance();
VueTools.expose2Instance(instance); VueTools.expose2Instance(instance);
const form = reactive({
formData: formData,
setComponentRef: setComponentRef,
instance: instance,
fieldStyleComputed: fieldStyleComputed,
updateModelValue: updateModelValue,
});
provide('form', form);
</script> </script>

389
io.sc.platform.core.frontend/src/platform/components/form/elements/WCodeMirror.vue

@ -1,389 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-field
ref="fieldRef"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:stack-label="stackLabelRef"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
style="position: relative"
@focus.stop.prevent="focus"
@blur.stop.prevent="blur"
>
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template>
<template #control>
<div v-if="editable && toolbar" class="w-full pt-2 border-b border-b-gray-200">
<Toolbar :source-code-editor="editorView" :user-defined-functions="userDefinedFunctions"></Toolbar>
</div>
<div
ref="codemirrorRef"
:style="`width:100%; padding-top: ${lineNumber ? 4 : 2}px; overflow: auto;`"
@focus.stop.prevent="() => {}"
@click.stop.prevent="() => {}"
></div>
</template>
<template v-if="!Tools.isEmpty(modelValueRef) && attrs.button" #append>
<q-btn
round
dense
flat
v-bind="attrs.button"
:style="{
content: '',
position: 'absolute',
bottom: '5px',
right: '5px',
}"
@click.stop.prevent="buttonClick(attrs.button)"
/>
</template>
</q-field>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs, onMounted, onUnmounted, onUpdated, watch, computed, toRaw } from 'vue';
import { Tools } from '@/platform';
import { FormValidators } from '@/platform/components';
import { EditorView } from '@codemirror/view';
import { EditorState, StateEffect, Compartment } from '@codemirror/state';
import * as view from '@codemirror/view';
import * as language from '@codemirror/language';
import * as commands from '@codemirror/commands';
import * as search from '@codemirror/search';
import * as autocomplete from '@codemirror/autocomplete';
import { LanguageSupport } from '@codemirror/language';
import { html, htmlLanguage } from '@codemirror/lang-html';
import { java, javaLanguage } from '@codemirror/lang-java';
import { javascript, javascriptLanguage } from '@codemirror/lang-javascript';
import { json, jsonLanguage } from '@codemirror/lang-json';
import { sql } from '@codemirror/lang-sql';
import { xml, xmlLanguage } from '@codemirror/lang-xml';
import PlaceholderPlugin from './w-code-mirror/PlaceholderPlugin';
import Toolbar from '@/platform/components/math/toolbar/Toolbar.vue';
const modelValueRef = defineModel({ type: String, default: '' });
const attrs = useAttrs();
const rules = attrs.rules;
const fieldRef = ref();
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
//
lang: { type: String, default: 'json' },
//
width: { type: [Number, String], default: '100%' },
//
height: { type: [Number, String], default: undefined },
//
lineHeight: { type: [Number, String], default: 20 },
//
rows: { type: Number, default: 4 },
//
toolbar: { type: Boolean, default: true },
//
editable: { type: Boolean, default: true },
//
lineWrap: { type: Boolean, default: false },
// tab size
tabSize: { type: Number, default: 4 },
//
lineNumber: { type: Boolean, default: false },
//
lineBreak: { type: Boolean, default: true },
//
//autoCompletion: { type: Boolean, default: false },
//
placeholder: { type: Boolean, default: false },
autoCompletion: {
type: Function,
default: undefined,
},
userDefinedFunctions: {
type: Array,
default: () => {
return [];
},
},
showIf: {
type: Function,
default: () => {
return true;
},
},
required: {
type: Boolean,
default: false,
},
requiredIf: {
type: Function,
default: undefined,
},
readOnlyIf: {
type: Function,
default: () => {
return false;
},
},
disableIf: {
type: Function,
default: () => {
return false;
},
},
form: {
type: Object,
default: undefined,
},
});
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (fieldRef?.value) {
fieldRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: modelValueRef.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: modelValueRef.value,
form: props.form,
}) || false
);
} else if (props.required) {
return true;
}
return false;
});
const readonlyIfComputed = computed(() => {
if (props.form && props.form.getStatus() === 'view') {
return true;
}
return props.readOnlyIf({
value: modelValueRef.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: modelValueRef.value,
form: props.form,
});
});
const basicSetup = [
EditorState.allowMultipleSelections.of(true),
//view.lineNumbers(),
//view.highlightActiveLine(),
view.highlightActiveLineGutter(),
view.highlightSpecialChars(),
view.drawSelection(),
view.dropCursor(),
view.rectangularSelection(),
view.crosshairCursor(),
commands.history(),
//language.foldGutter(),
language.indentOnInput(),
language.syntaxHighlighting(language.defaultHighlightStyle, { fallback: true }),
language.bracketMatching(),
//search.highlightSelectionMatches(),
view.keymap.of([
...commands.defaultKeymap,
...commands.historyKeymap,
...language.foldKeymap,
...autocomplete.completionKeymap,
...autocomplete.closeBracketsKeymap,
...search.searchKeymap,
commands.indentWithTab,
]),
];
const getLanguage = (lang: string): LanguageSupport => {
lang = lang || 'json';
switch (lang.toLowerCase()) {
case 'html':
return html();
case 'java':
return java();
case 'javascript':
return javascript();
case 'json':
return json();
case 'sql':
return sql();
case 'xml':
return xml();
default:
return json();
}
};
const codemirrorRef = ref();
// q-field stack-label field label
// field , label
// field , field label , label
const stackLabelRef = ref(!Tools.isUndefinedOrNull(modelValueRef.value));
let editorView;
let isFocus = false;
if (props.lineNumber) {
//
basicSetup.push(view.lineNumbers());
}
if (props.autoCompletion) {
//
basicSetup.push(autocomplete.closeBrackets());
basicSetup.push(
autocomplete.autocompletion({ override: [props.autoCompletion], activateOnCompletion: props.activateOnCompletion, maxRenderedOptions: 1000 }),
);
}
if (props.placeholder) {
//
basicSetup.push(PlaceholderPlugin);
}
if (props.lineWrap) {
basicSetup.push(EditorView.lineWrapping);
}
//
basicSetup.push(EditorView.editable.of(props.editable));
onMounted(() => {
if (codemirrorRef.value) {
let language = new Compartment();
let tabSize = new Compartment();
editorView = new EditorView({
extensions: [
basicSetup,
language.of(getLanguage(props.lang)),
tabSize.of(EditorState.tabSize.of(props.tabSize)),
EditorState.readOnly.of(props.readOnlyIf(props.form)),
EditorView.theme({
'&': {
outline: 'none !important',
width: Tools.px(props.width),
height: props.height ? Tools.px(props.height) : props.rows * 20 + (props.lineNumber ? 8 : 6) + 'px',
},
'.cm-content': {
'line-height': props.lineHeight + 'px',
},
}),
// , , blur
EditorView.updateListener.of(function (e) {
// emits('update:modelValue', e.state.doc.toString());
let content = e.state.doc.toString();
if (!props.lineBreak) {
content = content?.replace(/[\r\n]/g, '');
}
//updateModelValue(content);
}),
],
parent: codemirrorRef.value,
doc: modelValueRef.value,
});
watch(
() => modelValueRef.value,
() => {
// ,,
if (!isFocus) {
let content = modelValueRef.value;
if (!props.lineBreak) {
content = content.replace(/[\r\n]/g, '');
}
editorView.dispatch({ changes: { from: 0, to: editorView.state.doc.length, insert: content } });
}
},
);
}
});
onUnmounted(() => {
editorView.destroy();
});
const focus = () => {
isFocus = true;
stackLabelRef.value = true;
};
const blur = () => {
isFocus = false;
let content = editorView.state.doc.toString();
if (Tools.isUndefinedOrNull(content)) {
modelValueRef.value = '';
}
const reg = /\$\{(.+?)\}\.\$\{(.+?)\}/g;
while (reg.test(content)) {
content = content.replace(reg, '${$1.$2}');
}
modelValueRef.value = content;
if (content) {
stackLabelRef.value = true;
} else {
stackLabelRef.value = false;
}
};
const getValue = () => {
return editorView.state.doc.toString();
};
const setValue = (value: string) => {
editorView.dispatch({ changes: { from: 0, to: editorView.state.doc.length, insert: value } });
};
const configure = (values) => {
editorView.dispatch({ effects: StateEffect.reconfigure.of(values) });
};
const buttonClick = (button) => {
if (button.click) {
button.click({
value: modelValueRef.value,
form: props.form,
});
}
};
const validate = () => {
return fieldRef.value.validate();
};
defineExpose({
getValue,
setValue,
configure,
validate,
});
</script>

218
io.sc.platform.core.frontend/src/platform/components/form/elements/WCron.vue

@ -1,218 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-input
ref="cronRef"
v-model="valueRef"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
:v-bind="attrs"
@update:model-value="updateModelValue"
@change="changeValue"
>
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }} </template>
<template v-if="!props.form || (props.form && props.form.getStatus() !== 'view')" #append>
<!-- 选择颜色按钮 -->
<q-btn icon="bi-pencil-square" padding="2px" flat square unelevated :title="$t('select')">
<q-popup-proxy v-model:model-value="isShow" anchor="bottom right" self="top right" :offset="[0, 10]" style="width: 800px">
<q-tabs v-model="selectedTab" no-caps>
<q-tab name="second" :label="$t('second')" />
<q-tab name="minute" :label="$t('minute')" />
<q-tab name="hour" :label="$t('hour')" />
<q-tab name="day" :label="$t('day')" />
<q-tab name="month" :label="$t('month')" />
<q-tab name="week" :label="$t('week')" />
<q-tab name="year" :label="$t('year')" />
</q-tabs>
<q-tab-panels v-model="selectedTab" animated swipeable vertical transition-prev="jump-up" transition-next="jump-up" :keep-alive="true">
<q-tab-panel name="second">
<SecondSegment v-model="secondValueRef" @update:model-value="valueChanged"></SecondSegment>
</q-tab-panel>
<q-tab-panel name="minute">
<MinuteSegment v-model="minuteValueRef" @update:model-value="valueChanged"></MinuteSegment>
</q-tab-panel>
<q-tab-panel name="hour">
<HourSegment v-model="hourValueRef" @update:model-value="valueChanged"></HourSegment>
</q-tab-panel>
<q-tab-panel name="day">
<DaySegment v-model="dayValueRef" @update:model-value="valueChanged"></DaySegment>
</q-tab-panel>
<q-tab-panel name="month">
<MonthSegment v-model="monthValueRef" @update:model-value="valueChanged"></MonthSegment>
</q-tab-panel>
<q-tab-panel name="week">
<WeekSegment v-model="weekValueRef" @update:model-value="valueChanged"></WeekSegment>
</q-tab-panel>
<q-tab-panel name="year">
<YearSegment v-model="yearValueRef" @update:model-value="valueChanged"></YearSegment>
</q-tab-panel>
</q-tab-panels>
</q-popup-proxy>
</q-btn>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs, watch, computed } from 'vue';
import { Tools } from '@/platform';
import { FormValidators } from '@/platform/components';
import SecondSegment from './w-cron-segment/SecondSegment.vue';
import MinuteSegment from './w-cron-segment/MinuteSegment.vue';
import HourSegment from './w-cron-segment/HourSegment.vue';
import DaySegment from './w-cron-segment/DaySegment.vue';
import MonthSegment from './w-cron-segment/MonthSegment.vue';
import WeekSegment from './w-cron-segment/WeekSegment.vue';
import YearSegment from './w-cron-segment/YearSegment.vue';
const attrs = useAttrs();
const rules = attrs.rules;
const cronRef = ref();
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: String, default: '' },
showIf: {
type: Function,
default: () => {
return true;
},
},
required: {
type: Boolean,
default: false,
},
requiredIf: {
type: Function,
default: undefined,
},
readOnlyIf: {
type: Function,
default: () => {
return false;
},
},
disableIf: {
type: Function,
default: () => {
return false;
},
},
form: {
type: Object,
default: undefined,
},
});
const emit = defineEmits(['update:modelValue', 'change']);
const isShow = ref(false);
const selectedTab = ref('second');
const valueRef = ref(props.modelValue || '');
const splits = (props.modelValue || '').split(' ');
const secondValueRef = ref(splits[0]);
const minuteValueRef = ref(splits[1]);
const hourValueRef = ref(splits[2]);
const dayValueRef = ref(splits[3]);
const monthValueRef = ref(splits[4]);
const weekValueRef = ref(splits[5]);
const yearValueRef = ref(splits[6]);
watch(
() => props.modelValue,
(newVal, oldVal) => {
valueRef.value = newVal;
},
);
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (cronRef?.value) {
cronRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: valueRef.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: valueRef.value,
form: props.form,
}) || false
);
} else if (props.required) {
return true;
}
return false;
});
const readonlyIfComputed = computed(() => {
if (props.form && props.form.getStatus() === 'view') {
return true;
}
return props.readOnlyIf({
value: valueRef.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: valueRef.value,
form: props.form,
});
});
const valueChanged = () => {
const values = [];
values.push(secondValueRef.value ? secondValueRef.value : '*');
values.push(minuteValueRef.value ? minuteValueRef.value : '*');
values.push(hourValueRef.value ? hourValueRef.value : '*');
values.push(dayValueRef.value ? dayValueRef.value : '*');
values.push(monthValueRef.value ? monthValueRef.value : '*');
values.push(weekValueRef.value ? weekValueRef.value : '*');
values.push(yearValueRef.value ? yearValueRef.value : '*');
const result = Tools.join(values, ' ');
valueRef.value = result;
emit('update:modelValue', result);
changeValue(result);
};
const updateModelValue = (value) => {
valueRef.value = value;
emit('update:modelValue', value);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const validate = () => {
return cronRef.value.validate();
};
defineExpose({
validate,
});
</script>

76
io.sc.platform.core.frontend/src/platform/components/form/elements/w-code-mirror/PlaceholderPlugin.ts

@ -1,76 +0,0 @@
import { EditorView, ViewPlugin, ViewUpdate, MatchDecorator, Decoration, DecorationSet, WidgetType } from '@codemirror/view';
class PlaceholderWidget extends WidgetType {
constructor(name) {
super();
this.name = name;
}
eq(other) {
return this.name == other.name;
}
toDOM() {
const container = document.createElement('span');
container.className = 'px-0.5';
const elt = document.createElement('span');
elt.setAttribute('placeholder', true);
elt.className = 'p-0.5 border border-gray-800 rounded-md';
elt.textContent = this.name;
container.appendChild(elt);
return container;
}
ignoreEvent() {
return false;
}
}
const placeholderMatcher = new MatchDecorator({
regexp: /\$\{(.+?)\}/g,
decoration: (match) =>
Decoration.replace({
widget: new PlaceholderWidget(match[1]),
}),
});
const placeholderPlugin = ViewPlugin.fromClass(
class {
placeholders: DecorationSet;
constructor(view: EditorView) {
this.placeholders = placeholderMatcher.createDeco(view);
}
update(update: ViewUpdate) {
this.placeholders = placeholderMatcher.updateDeco(update, this.placeholders);
}
},
{
decorations: (instance) => instance.placeholders,
provide: (plugin) =>
EditorView.atomicRanges.of((view) => {
return view.plugin(plugin)?.placeholders || Decoration.none;
}),
eventHandlers: {
mouseover: (e, view) => {
const target = e.target as HTMLElement;
if (target.tagName.toLowerCase() === 'span' && target.getAttribute('placeholder')) {
target.className = 'p-0.5 border border-orange-400 rounded-md';
}
},
mouseout: (e, view) => {
const target = e.target as HTMLElement;
if (target.tagName.toLowerCase() === 'span' && target.getAttribute('placeholder')) {
target.className = 'p-0.5 border border-gray-800 rounded-md';
}
},
contextmenu: (e, view) => {
e.preventDefault();
console.log(view);
},
},
},
);
export default placeholderPlugin;

251
io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/DaySegment.vue

@ -1,251 +0,0 @@
<template>
<q-list dense style="width: 100%">
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.day.per') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="notSpecify" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.day.notSpecify') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.day.period.1') }}</span>
<q-input
v-model="periodFromRef"
type="number"
:disable="!periodFromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.day.period.2') }}</span>
<q-input
v-model="periodToRef"
type="number"
:disable="!periodToEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.day.period.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per2" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.day.per2.1') }}</span>
<q-input
v-model="per2FromRef"
type="number"
:disable="!per2FromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.day.per2.2') }}</span>
<q-input
v-model="per2PerRef"
type="number"
:disable="!per2PerEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.day.per2.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="workDay" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.day.workDay.1') }}</span>
<q-input
v-model="workDayRef"
type="number"
:disable="!workDayEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.day.workDay.2') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="lastDay" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.day.lastDay') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.day.specify') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label>
<div class="row">
<template v-for="index in 31" :key="index">
<template v-if="index % 10 === 1">
<div class="col-1"></div>
</template>
<div class="col-1">
<q-checkbox
v-model="optionsRef[index - 1]"
dense
:label="'' + index"
:disable="!optionsEnableRef"
@update:model-value="valueChanged"
></q-checkbox>
</div>
<template v-if="index % 10 === 0">
<div class="col-1"></div>
</template>
</template>
</div>
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Tools } from '@/platform';
const props = defineProps({
modelValue: { type: String, default: '' },
});
const emit = defineEmits(['update:modelValue']);
const valueRef = ref(props.modelValue);
const modeRef = ref('');
const periodFromEnableRef = ref(false);
const periodFromRef = ref('');
const periodToEnableRef = ref(false);
const periodToRef = ref('');
const per2FromEnableRef = ref(false);
const per2FromRef = ref('');
const per2PerEnableRef = ref(false);
const per2PerRef = ref('');
const workDayEnableRef = ref(false);
const workDayRef = ref('');
const optionsEnableRef = ref(false);
const optionsRef = ref([]);
for (let i = 0; i < 31; i++) {
optionsRef.value.push(false);
}
const valueChanged = () => {
if (modeRef.value === 'per') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
workDayEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'period') {
periodFromEnableRef.value = true;
periodToEnableRef.value = true;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
workDayEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'per2') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = true;
per2PerEnableRef.value = true;
workDayEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'workDay') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
workDayEnableRef.value = true;
optionsEnableRef.value = false;
} else if (modeRef.value === 'lastDay') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
workDayEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'specify') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
workDayEnableRef.value = false;
optionsEnableRef.value = true;
}
if (modeRef.value === 'per') {
valueRef.value = '*';
} else if (modeRef.value === 'period') {
valueRef.value = periodFromRef.value + '-' + periodToRef.value;
} else if (modeRef.value === 'per2') {
valueRef.value = per2FromRef.value + '/' + per2PerRef.value;
} else if (modeRef.value === 'workDay') {
valueRef.value = workDayRef.value + 'W';
} else if (modeRef.value === 'lastDay') {
valueRef.value = 'L';
} else if (modeRef.value === 'specify') {
let seconds = [];
for (let i = 0; i < 31; i++) {
if (optionsRef.value[i]) {
seconds.push(i + 1);
}
}
valueRef.value = Tools.join(seconds, ',');
}
emit('update:modelValue', valueRef.value);
};
</script>

185
io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/HourSegment.vue

@ -1,185 +0,0 @@
<template>
<q-list dense style="width: 100%">
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.hour.per') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.hour.period.1') }}</span>
<q-input
v-model="periodFromRef"
type="number"
:disable="!periodFromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.hour.period.2') }}</span>
<q-input
v-model="periodToRef"
type="number"
:disable="!periodToEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.hour.period.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per2" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.hour.per2.1') }}</span>
<q-input
v-model="per2FromRef"
type="number"
:disable="!per2FromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.hour.per2.2') }}</span>
<q-input
v-model="per2PerRef"
type="number"
:disable="!per2PerEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.hour.per2.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.hour.specify') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-item-label>
<div class="row">
<template v-for="index in 24" :key="index">
<template v-if="index % 8 === 1">
<div class="col-1"></div>
</template>
<div class="col-1">
<q-checkbox
v-model="optionsRef[index - 1]"
dense
:label="'' + (index - 1)"
:disable="!optionsEnableRef"
@update:model-value="valueChanged"
></q-checkbox>
</div>
<template v-if="index % 8 === 0">
<div class="col-3"></div>
</template>
</template>
</div>
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Tools } from '@/platform';
const props = defineProps({
modelValue: { type: String, default: '' },
});
const emit = defineEmits(['update:modelValue']);
const valueRef = ref(props.modelValue);
const modeRef = ref('');
const periodFromEnableRef = ref(false);
const periodFromRef = ref('');
const periodToEnableRef = ref(false);
const periodToRef = ref('');
const per2FromEnableRef = ref(false);
const per2FromRef = ref('');
const per2PerEnableRef = ref(false);
const per2PerRef = ref('');
const optionsEnableRef = ref(false);
const optionsRef = ref([]);
for (let i = 0; i < 24; i++) {
optionsRef.value.push(false);
}
const valueChanged = () => {
if (modeRef.value === 'per') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'period') {
periodFromEnableRef.value = true;
periodToEnableRef.value = true;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'per2') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = true;
per2PerEnableRef.value = true;
optionsEnableRef.value = false;
} else if (modeRef.value === 'specify') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = true;
}
if (modeRef.value === 'per') {
valueRef.value = '*';
} else if (modeRef.value === 'period') {
valueRef.value = periodFromRef.value + '-' + periodToRef.value;
} else if (modeRef.value === 'per2') {
valueRef.value = per2FromRef.value + '/' + per2PerRef.value;
} else if (modeRef.value === 'specify') {
let seconds = [];
for (let i = 0; i < 24; i++) {
if (optionsRef.value[i]) {
seconds.push(i);
}
}
valueRef.value = Tools.join(seconds, ',');
}
emit('update:modelValue', valueRef.value);
};
</script>

185
io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/MinuteSegment.vue

@ -1,185 +0,0 @@
<template>
<q-list dense style="width: 100%">
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.minute.per') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.minute.period.1') }}</span>
<q-input
v-model="periodFromRef"
type="number"
:disable="!periodFromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.minute.period.2') }}</span>
<q-input
v-model="periodToRef"
type="number"
:disable="!periodToEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.minute.period.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per2" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.minute.per2.1') }}</span>
<q-input
v-model="per2FromRef"
type="number"
:disable="!per2FromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.minute.per2.2') }}</span>
<q-input
v-model="per2PerRef"
type="number"
:disable="!per2PerEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.minute.per2.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.minute.specify') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-item-label>
<div class="row">
<template v-for="index in 60" :key="index">
<template v-if="index % 10 === 1">
<div class="col-1"></div>
</template>
<div class="col-1">
<q-checkbox
v-model="optionsRef[index - 1]"
dense
:label="'' + (index - 1)"
:disable="!optionsEnableRef"
@update:model-value="valueChanged"
></q-checkbox>
</div>
<template v-if="index % 10 === 0">
<div class="col-1"></div>
</template>
</template>
</div>
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Tools } from '@/platform';
const props = defineProps({
modelValue: { type: String, default: '' },
});
const emit = defineEmits(['update:modelValue']);
const valueRef = ref(props.modelValue);
const modeRef = ref('');
const periodFromEnableRef = ref(false);
const periodFromRef = ref('');
const periodToEnableRef = ref(false);
const periodToRef = ref('');
const per2FromEnableRef = ref(false);
const per2FromRef = ref('');
const per2PerEnableRef = ref(false);
const per2PerRef = ref('');
const optionsEnableRef = ref(false);
const optionsRef = ref([]);
for (let i = 0; i < 60; i++) {
optionsRef.value.push(false);
}
const valueChanged = () => {
if (modeRef.value === 'per') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'period') {
periodFromEnableRef.value = true;
periodToEnableRef.value = true;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'per2') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = true;
per2PerEnableRef.value = true;
optionsEnableRef.value = false;
} else if (modeRef.value === 'specify') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = true;
}
if (modeRef.value === 'per') {
valueRef.value = '*';
} else if (modeRef.value === 'period') {
valueRef.value = periodFromRef.value + '-' + periodToRef.value;
} else if (modeRef.value === 'per2') {
valueRef.value = per2FromRef.value + '/' + per2PerRef.value;
} else if (modeRef.value === 'specify') {
let seconds = [];
for (let i = 0; i < 60; i++) {
if (optionsRef.value[i]) {
seconds.push(i);
}
}
valueRef.value = Tools.join(seconds, ',');
}
emit('update:modelValue', valueRef.value);
};
</script>

197
io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/MonthSegment.vue

@ -1,197 +0,0 @@
<template>
<q-list dense style="width: 100%">
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.month.per') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="notSpecify" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.month.notSpecify') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.month.period.1') }}</span>
<q-input
v-model="periodFromRef"
type="number"
:disable="!periodFromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.month.period.2') }}</span>
<q-input
v-model="periodToRef"
type="number"
:disable="!periodToEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.month.period.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per2" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.month.per2.1') }}</span>
<q-input
v-model="per2FromRef"
type="number"
:disable="!per2FromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.month.per2.2') }}</span>
<q-input
v-model="per2PerRef"
type="number"
:disable="!per2PerEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.month.per2.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.month.specify') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label>
<div class="row">
<template v-for="index in 12" :key="index">
<template v-if="index % 6 === 1">
<div class="col-1"></div>
</template>
<div class="col-1">
<q-checkbox
v-model="optionsRef[index - 1]"
dense
:label="'' + index"
:disable="!optionsEnableRef"
@update:model-value="valueChanged"
></q-checkbox>
</div>
<template v-if="index % 6 === 0">
<div class="col-5"></div>
</template>
</template>
</div>
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Tools } from '@/platform';
const props = defineProps({
modelValue: { type: String, default: '' },
});
const emit = defineEmits(['update:modelValue']);
const valueRef = ref(props.modelValue);
const modeRef = ref('');
const periodFromEnableRef = ref(false);
const periodFromRef = ref('');
const periodToEnableRef = ref(false);
const periodToRef = ref('');
const per2FromEnableRef = ref(false);
const per2FromRef = ref('');
const per2PerEnableRef = ref(false);
const per2PerRef = ref('');
const workDayEnableRef = ref(false);
const workDayRef = ref('');
const optionsEnableRef = ref(false);
const optionsRef = ref([]);
for (let i = 0; i < 12; i++) {
optionsRef.value.push(false);
}
const valueChanged = () => {
if (modeRef.value === 'per') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'period') {
periodFromEnableRef.value = true;
periodToEnableRef.value = true;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'per2') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = true;
per2PerEnableRef.value = true;
optionsEnableRef.value = false;
} else if (modeRef.value === 'specify') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = true;
}
if (modeRef.value === 'per') {
valueRef.value = '*';
} else if (modeRef.value === 'period') {
valueRef.value = periodFromRef.value + '-' + periodToRef.value;
} else if (modeRef.value === 'per2') {
valueRef.value = per2FromRef.value + '/' + per2PerRef.value;
} else if (modeRef.value === 'specify') {
let seconds = [];
for (let i = 0; i < 12; i++) {
if (optionsRef.value[i]) {
seconds.push(i + 1);
}
}
valueRef.value = Tools.join(seconds, ',');
}
emit('update:modelValue', valueRef.value);
};
</script>

185
io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/SecondSegment.vue

@ -1,185 +0,0 @@
<template>
<q-list dense style="width: 100%">
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.second.per') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.second.period.1') }}</span>
<q-input
v-model="periodFromRef"
type="number"
:disable="!periodFromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.second.period.2') }}</span>
<q-input
v-model="periodToRef"
type="number"
:disable="!periodToEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.second.period.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per2" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.second.per2.1') }}</span>
<q-input
v-model="per2FromRef"
type="number"
:disable="!per2FromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.second.per2.2') }}</span>
<q-input
v-model="per2PerRef"
type="number"
:disable="!per2PerEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.second.per2.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.second.specify') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-item-label>
<div class="row">
<template v-for="index in 60" :key="index">
<template v-if="index % 10 === 1">
<div class="col-1"></div>
</template>
<div class="col-1">
<q-checkbox
v-model="optionsRef[index - 1]"
dense
:label="'' + (index - 1)"
:disable="!optionsEnableRef"
@update:model-value="valueChanged"
></q-checkbox>
</div>
<template v-if="index % 10 === 0">
<div class="col-1"></div>
</template>
</template>
</div>
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Tools } from '@/platform';
const props = defineProps({
modelValue: { type: String, default: '' },
});
const emit = defineEmits(['update:modelValue']);
const valueRef = ref(props.modelValue);
const modeRef = ref('');
const periodFromEnableRef = ref(false);
const periodFromRef = ref('');
const periodToEnableRef = ref(false);
const periodToRef = ref('');
const per2FromEnableRef = ref(false);
const per2FromRef = ref('');
const per2PerEnableRef = ref(false);
const per2PerRef = ref('');
const optionsEnableRef = ref(false);
const optionsRef = ref([]);
for (let i = 0; i < 60; i++) {
optionsRef.value.push(false);
}
const valueChanged = () => {
if (modeRef.value === 'per') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'period') {
periodFromEnableRef.value = true;
periodToEnableRef.value = true;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'per2') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = true;
per2PerEnableRef.value = true;
optionsEnableRef.value = false;
} else if (modeRef.value === 'specify') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
per2FromEnableRef.value = false;
per2PerEnableRef.value = false;
optionsEnableRef.value = true;
}
if (modeRef.value === 'per') {
valueRef.value = '*';
} else if (modeRef.value === 'period') {
valueRef.value = periodFromRef.value + '-' + periodToRef.value;
} else if (modeRef.value === 'per2') {
valueRef.value = per2FromRef.value + '/' + per2PerRef.value;
} else if (modeRef.value === 'specify') {
let seconds = [];
for (let i = 0; i < 60; i++) {
if (optionsRef.value[i]) {
seconds.push(i);
}
}
valueRef.value = Tools.join(seconds, ',');
}
emit('update:modelValue', valueRef.value);
};
</script>

233
io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/WeekSegment.vue

@ -1,233 +0,0 @@
<template>
<q-list dense style="width: 100%">
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.week.per') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="notSpecify" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.week.notSpecify') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.week.period.1') }}</span>
<q-input
v-model="periodFromRef"
type="number"
:disable="!periodFromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.week.period.2') }}</span>
<q-input
v-model="periodToRef"
type="number"
:disable="!periodToEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.week.period.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="weekOfYear" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.week.weekOfYear.1') }}</span>
<q-input
v-model="weekOfYearRef"
type="number"
:disable="!weekOfYearEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.week.weekOfYear.2') }}</span>
<q-input
v-model="dayOfWeekRef"
type="number"
:disable="!dayOfWeekEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.week.weekOfYear.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="lastWeek" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.week.lastDay.1') }}</span>
<q-input
v-model="lastWeekRef"
type="number"
:disable="!lastWeekEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.week.lastDay.2') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.week.specify') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label>
<div class="row">
<template v-for="index in 7" :key="index">
<template v-if="index % 7 === 1">
<div class="col-1"></div>
</template>
<div class="col-1">
<q-checkbox
v-model="optionsRef[index - 1]"
dense
:label="'' + index"
:disable="!optionsEnableRef"
@update:model-value="valueChanged"
></q-checkbox>
</div>
<template v-if="index % 7 === 0">
<div class="col-5"></div>
</template>
</template>
</div>
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Tools } from '@/platform';
const props = defineProps({
modelValue: { type: String, default: '' },
});
const emit = defineEmits(['update:modelValue']);
const valueRef = ref(props.modelValue);
const modeRef = ref('');
const periodFromEnableRef = ref(false);
const periodFromRef = ref('');
const periodToEnableRef = ref(false);
const periodToRef = ref('');
const weekOfYearEnableRef = ref(false);
const weekOfYearRef = ref('');
const dayOfWeekEnableRef = ref(false);
const dayOfWeekRef = ref('');
const lastWeekEnableRef = ref(false);
const lastWeekRef = ref('');
const optionsEnableRef = ref(false);
const optionsRef = ref([]);
for (let i = 0; i < 7; i++) {
optionsRef.value.push(false);
}
const valueChanged = () => {
if (modeRef.value === 'per') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
weekOfYearEnableRef.value = false;
dayOfWeekEnableRef.value = false;
lastWeekEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'period') {
periodFromEnableRef.value = true;
periodToEnableRef.value = true;
weekOfYearEnableRef.value = false;
dayOfWeekEnableRef.value = false;
lastWeekEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'weekOfYear') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
weekOfYearEnableRef.value = true;
dayOfWeekEnableRef.value = true;
lastWeekEnableRef.value = false;
optionsEnableRef.value = false;
} else if (modeRef.value === 'lastWeek') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
weekOfYearEnableRef.value = false;
dayOfWeekEnableRef.value = false;
lastWeekEnableRef.value = true;
optionsEnableRef.value = false;
} else if (modeRef.value === 'specify') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
weekOfYearEnableRef.value = false;
dayOfWeekEnableRef.value = false;
lastWeekEnableRef.value = false;
optionsEnableRef.value = true;
}
if (modeRef.value === 'per') {
valueRef.value = '*';
} else if (modeRef.value === 'period') {
valueRef.value = periodFromRef.value + '-' + periodToRef.value;
} else if (modeRef.value === 'weekOfYear') {
valueRef.value = weekOfYearRef.value + '/' + dayOfWeekRef.value;
} else if (modeRef.value === 'lastWeek') {
valueRef.value = lastWeekRef.value + 'L';
} else if (modeRef.value === 'specify') {
let seconds = [];
for (let i = 0; i < 7; i++) {
if (optionsRef.value[i]) {
seconds.push(i + 1);
}
}
valueRef.value = Tools.join(seconds, ',');
}
emit('update:modelValue', valueRef.value);
};
</script>

93
io.sc.platform.core.frontend/src/platform/components/form/elements/w-cron-segment/YearSegment.vue

@ -1,93 +0,0 @@
<template>
<q-list dense style="width: 100%">
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.year.per') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="notSpecify" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>{{ $t('cron.year.notSpecify') }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" />
</q-item-section>
<q-item-section>
<q-item-label>
<div class="row no-wrap items-center">
<span class="pr-2">{{ $t('cron.year.period.1') }}</span>
<q-input
v-model="periodFromRef"
type="number"
:disable="!periodFromEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.year.period.2') }}</span>
<q-input
v-model="periodToRef"
type="number"
:disable="!periodToEnableRef"
outlined
dense
style="width: 100px"
@update:model-value="valueChanged"
></q-input>
<span class="px-2">{{ $t('cron.year.period.3') }}</span>
</div>
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Tools } from '@/platform';
const props = defineProps({
modelValue: { type: String, default: '' },
});
const emit = defineEmits(['update:modelValue']);
const valueRef = ref(props.modelValue);
const modeRef = ref('');
const periodFromEnableRef = ref(false);
const periodFromRef = ref('');
const periodToEnableRef = ref(false);
const periodToRef = ref('');
const valueChanged = () => {
if (modeRef.value === 'per') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
} else if (modeRef.value === 'notSpecify') {
periodFromEnableRef.value = false;
periodToEnableRef.value = false;
} else if (modeRef.value === 'period') {
periodFromEnableRef.value = true;
periodToEnableRef.value = true;
}
if (modeRef.value === 'per') {
valueRef.value = '*';
} else if (modeRef.value === 'notSpecify') {
valueRef.value = '';
} else if (modeRef.value === 'period') {
valueRef.value = periodFromRef.value + '-' + periodToRef.value;
}
emit('update:modelValue', valueRef.value);
};
</script>

5
io.sc.platform.core.frontend/src/platform/components/form/elements/w-query-condition/WExpression.vue

@ -1,5 +0,0 @@
<template>
<div></div>
</template>
<script setup lang="ts">
</script>

47
io.sc.platform.core.frontend/src/platform/components/form/ts/ComputedManager.ts

@ -0,0 +1,47 @@
import { Screen } from 'quasar';
import { computed } from 'vue';
import { Form } from './Form';
/**
*
*/
export class ComputedManager {
form: Form;
/**
*
*/
screenCols = { xs: 1, sm: 2, md: 3, lg: 4, xl: 6 };
constructor(form_: Form) {
this.form = form_;
}
/**
*
*/
screenColsNumComputed = computed(() => {
if (typeof this.form.props.colsNum === 'number' && this.form.props.colsNum > 0) {
return this.form.props.colsNum;
} else if (typeof this.form.props.colsNum === 'object') {
const screen = { ...this.screenCols, ...this.form.props.colsNum };
return screen[Screen.name];
}
return this.screenCols[Screen.name];
});
/**
*
*/
formLayoutStyleComputed = computed(() => {
const style = {};
if (typeof this.form.props.colsNum === 'number' && this.form.props.colsNum > 0) {
style['grid-template-columns'] = 'repeat(' + this.form.props.colsNum + ', minmax(0, 1fr))';
} else {
style['grid-template-columns'] = 'repeat(' + this.screenColsNumComputed.value + ', minmax(0, 1fr))';
}
style['column-gap'] = this.form.props.xGap + 'px';
style['row-gap'] = this.form.props.yGap + 'px';
return style;
});
}

14
io.sc.platform.core.frontend/src/platform/components/form/ts/Constant.ts

@ -0,0 +1,14 @@
/**
*
*/
export class Constant {
/**
*
*/
static STATUS_ADD = 'add';
/**
*
*/
static GROUP_TYPE = 'w-form-group';
}

189
io.sc.platform.core.frontend/src/platform/components/form/ts/ExposeApiManager.ts

@ -0,0 +1,189 @@
import { toRaw } from 'vue';
import { Tools, $t } from '@/platform';
import { getDefaultValue } from '../FormField.ts';
import { Form } from './Form.ts';
import { ErrorType } from './types/ErrorType.ts';
/**
* API管理器
*/
export class ExposeApiManager {
form: Form;
constructor(form_: Form) {
this.form = form_;
this.getData = this.getData.bind(this);
this.setData = this.setData.bind(this);
this.getStatus = this.getStatus.bind(this);
this.setStatus = this.setStatus.bind(this);
this.reset = this.reset.bind(this);
this.validate = this.validate.bind(this);
this.setFieldValue = this.setFieldValue.bind(this);
this.getFieldValue = this.getFieldValue.bind(this);
this.getColsNum = this.getColsNum.bind(this);
this.setValidationErrors = this.setValidationErrors.bind(this);
this.getFieldComponent = this.getFieldComponent.bind(this);
this.resetValidation = this.resetValidation.bind(this);
this.getFields = this.getFields.bind(this);
}
/**
*
* @returns
*/
getData() {
const data = { ...toRaw(this.form.data) };
if (!Tools.isEmpty(this.form.props.modelValue)) {
// 对于用户自定义绑定模型值的,为了避免页面未填写值的字段不会更新到模型值中,返回数据值时将其合并。
const modelValue = this.form.buildModelValueByFields();
return { ...modelValue, ...data };
}
return data;
}
/**
*
* @param data (JSON格式)
*/
setData(data: any) {
Object.keys(this.form.fields).forEach((key: string) => {
if (Tools.hasOwnProperty(this.form.data, key)) {
if (Tools.isEmpty(data)) {
this.form.data[key] = getDefaultValue(this.form.fields[key]);
} else if (data[key] !== undefined) {
this.form.data[key] = data[key];
}
}
});
}
/**
*
* @returns
*/
getStatus() {
return this.form.status;
}
/**
*
* @param status
*/
setStatus(status: string) {
this.form.status = status;
}
/**
*
*/
reset() {
Object.keys(this.form.data).forEach((key) => {
this.form.data[key] = getDefaultValue(this.form.fields[key]);
});
}
/**
*
*/
async validate() {
const result = await this.formValidate();
return result;
}
private async formValidate() {
let validate = false;
await this.form.cf
.getFormRef()
.validate()
.then((success) => {
if (success) {
validate = true;
}
});
return validate;
}
/**
*
* @param fieldName
* @param value
*/
setFieldValue(fieldName: string, value: any) {
this.form.data[fieldName] = value;
}
/**
*
* @param fieldName
* @returns
*/
getFieldValue(fieldName: string) {
return this.form.data[fieldName];
}
/**
*
* @returns
*/
getColsNum() {
return this.form.cm.screenColsNumComputed.value;
}
/**
*
* @param errors
*/
setValidationErrors(errors: ErrorType[]) {
if (errors && errors.length > 0) {
const grouped = {};
errors.map(({ fieldName, ...rest }) => {
grouped[fieldName] = grouped[fieldName] || [];
grouped[fieldName].push(rest);
});
for (const name in grouped) {
const field = this.form.fieldArrayFindByName(name);
if (field) {
field.error = true;
field.errorMessage = grouped[name]
.map((obj) => {
if (!Tools.isEmpty(obj['errorMessageI18nKey'])) {
return $t(obj['errorMessageI18nKey']);
}
return obj.errorMessage;
})
.join('、');
}
}
}
}
/**
* 使
* @param fieldName
* @returns
*/
getFieldComponent(fieldName: string) {
return this.form.componentRef[fieldName];
}
/**
*
*/
resetValidation() {
this.form.cf.getFormRef().resetValidation();
this.form.fieldArrayRecursiveHandler(this.form.fieldArray.value, (field: any) => {
if (Tools.hasOwnProperty(field, 'error') && field['error']) {
field['error'] = false;
}
});
}
/**
*
* @returns
*/
getFields() {
const result = {};
this.form.fieldArrayRecursiveHandler(this.form.fieldArray.value, (field: any) => {
result[field.name] = field;
});
return result;
}
}

229
io.sc.platform.core.frontend/src/platform/components/form/ts/Form.ts

@ -0,0 +1,229 @@
import { Reactive, Ref, ref, reactive } from 'vue';
import { Tools } from '@/platform';
import { getDefaultValue } from '../FormField.ts';
import { Constant } from './Constant.ts';
import { PropsType } from './types/PropsType.ts';
import { ComputedManager } from './ComputedManager.ts';
import { ExposeApiManager } from './ExposeApiManager.ts';
import { ComponentFunctionsType } from './types/ComponentFunctionsType.ts';
/**
* w-form组件类
*/
export class Form {
/**
*
*/
instance?: any;
/**
*
*/
props: PropsType;
/**
*
*/
data: Reactive<any> = reactive({});
/**
*
*/
fieldArray: Ref = ref([]);
/**
* 便 name key value name w-form-group
*/
fields: Reactive<any> = reactive({});
/**
* setStatus getStatus
*/
status: string = Constant.STATUS_ADD;
/**
* 使 ref ref key name
*/
componentRef: Reactive<any> = reactive({});
/**
*
*/
cm: ComputedManager;
/**
* API管理器
*/
api: ExposeApiManager;
/**
* vue文件中定义的函数
*/
cf: ComponentFunctionsType = {
getFormRef: () => {},
updateModelValue: () => {},
};
setInstance(instance_: any) {
this.instance = instance_;
}
constructor(props_: PropsType) {
this.props = props_;
// 初始化
this.cm = new ComputedManager(this);
this.api = new ExposeApiManager(this);
if (this.props.fields) {
this.buildFieldsAndData();
}
this.getFieldStyle = this.getFieldStyle.bind(this);
this.setComponentRef = this.setComponentRef.bind(this);
}
/**
* ref
* @param el html元素对象
* @param fieldName
*/
setComponentRef(el: Element, fieldName: string) {
if (el) {
this.componentRef[fieldName] = el;
}
}
/**
*
* @param field
* @returns
*/
getFieldStyle(field: any) {
const style = {};
if (field) {
const screenColsNum = this.cm?.screenColsNumComputed.value;
if (field.firstCol) {
style['grid-column-start'] = 1;
if (field.colSpan === 'full' || (typeof field.colSpan === 'number' && screenColsNum < field.colSpan)) {
style['grid-column-end'] = `${screenColsNum + 1}`;
} else if (typeof field.colSpan === 'number' && field.colSpan > 0) {
style['grid-column-end'] = `${field.colSpan + 1}`;
}
} else {
if (field.colSpan === 'full') {
// col-span-${screenColsNumComputed.value}
style['grid-column'] = `span ${screenColsNum} / span ${screenColsNum}`;
} else {
if (field.colSpan && screenColsNum >= field.colSpan) {
// col-span-${field.colSpan}
style['grid-column'] = `span ${field.colSpan} / span ${field.colSpan}`;
} else {
// col-span-1
style['grid-column'] = 'span 1 / span 1';
}
}
}
}
return style;
}
/**
* Base fields fieldArray
*/
buildFields() {
this.fieldArray.value = [];
this.fields = reactive({});
this.fieldsHandler(this.props.fields, true);
}
/**
* Base fields fieldArray
*/
buildFieldsAndData() {
this.buildFields();
this.buildData();
}
/**
*
* @param arr
* @param isRoot
*/
private fieldsHandler(arr: Array<any>, isRoot: boolean) {
arr.forEach((field: any) => {
if (isRoot) {
this.fieldArray.value.push({ ...field });
}
if (field.type === Constant.GROUP_TYPE && field.fields) {
this.fieldsHandler(field.fields, false);
} else if (Tools.hasOwnProperty(field, 'name')) {
this.fields[field.name] = { ...field };
}
});
}
/**
*
*/
private buildData() {
if (!Tools.isEmpty(this.props.modelValue)) {
// 用户进行了双向绑定,数据以 modelValue 为准。
this.data = reactive(this.props.modelValue);
} else {
// 根据 fields 构建表单初始数据对象
const modelValue = this.buildModelValueByFields();
this.data = reactive(modelValue);
}
}
/**
*
* @returns
*/
buildModelValueByFields() {
const modelValue = {};
Object.keys(this.fields).forEach((fieldName: string) => {
modelValue[fieldName] = getDefaultValue(this.fields[fieldName]);
});
return modelValue;
}
/**
*
* @param fieldName
* @returns
*/
fieldArrayFindByName(fieldName: string) {
return this.recursiveGetField(this.fieldArray.value, fieldName);
}
/**
*
* @param arr
* @param fieldName
* @returns
*/
private recursiveGetField(arr: Array<any>, fieldName: string) {
let result: any = undefined;
arr.forEach((field: any) => {
if (field.type === Constant.GROUP_TYPE && field.fields) {
const field_ = this.recursiveGetField(field.fields, fieldName);
if (field_) {
result = field_;
}
} else if (Tools.hasOwnProperty(field, 'name') && field.name === fieldName) {
result = field;
}
});
return result;
}
/**
*
* @param arr
* @param fieldName
* @param callBack
* @returns
*/
fieldArrayRecursiveHandler(arr: Array<any>, callBack: any, fieldName?: string) {
arr.forEach((field: any) => {
if (field.type === Constant.GROUP_TYPE && field.fields) {
this.fieldArrayRecursiveHandler(field.fields, callBack, fieldName);
} else if (Tools.hasOwnProperty(field, 'name')) {
if (Tools.isEmpty(fieldName)) {
callBack(field);
} else if (field.name === fieldName) {
callBack(field);
}
}
});
}
}

10
io.sc.platform.core.frontend/src/platform/components/form/ts/types/ComponentFunctionsType.ts

@ -0,0 +1,10 @@
export type ComponentFunctionsType = {
/**
* ref函数
*/
getFormRef: Function;
/**
*
*/
updateModelValue: Function;
}

4
io.sc.platform.core.frontend/src/platform/components/form/ts/types/ErrorType.ts

@ -0,0 +1,4 @@
export type ErrorType = {
fieldName: string;
errorMessage: string;
}

22
io.sc.platform.core.frontend/src/platform/components/form/ts/types/PropsType.ts

@ -0,0 +1,22 @@
export type PropsType = {
/**
* form模型值
*/
modelValue?: any,
/**
* 0
*/
colsNum?: any,
/**
* X轴两个元素间的空隙像素点
*/
xGap?: number,
/**
* Y轴两个元素间的空隙像素点
*/
yGap?: number,
/**
*
*/
fields: Array<any>,
}

2
io.sc.platform.core.frontend/src/platform/components/grid/Td.vue

@ -75,7 +75,7 @@ const titleComputed = computed(() => {
return column['title']; return column['title'];
} }
} }
if (props.col.classes?.indexOf('truncate') > -1 && !Tools.isEmpty(props.col.value) && typeof props.col.value !== 'object') { if (!Tools.isEmpty(props.col.value) && typeof props.col.value !== 'object') {
return props.col.value; return props.col.value;
} }
return ''; return '';

21
io.sc.platform.core.frontend/src/platform/components/grid/extra/config/ConfigPanel.vue

@ -14,6 +14,10 @@
<q-separator /> <q-separator />
</template> </template>
<!-- 斑马纹 -->
<Stripe></Stripe>
<q-separator />
<!-- 分割线 --> <!-- 分割线 -->
<Separator></Separator> <Separator></Separator>
<q-separator /> <q-separator />
@ -40,14 +44,15 @@
import { inject, computed, ref } from 'vue'; import { inject, computed, ref } from 'vue';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { Constant, GridTools } from '../../ts/index'; import { Constant, GridTools } from '../../ts/index';
import CheckboxSelection from './CheckboxSelection.vue'; import CheckboxSelection from './src/CheckboxSelection.vue';
import Dense from './Dense.vue'; import Dense from './src/Dense.vue';
import DisplayColumn from './DisplayColumn.vue'; import DisplayColumn from './src/DisplayColumn.vue';
import Fullscreen from './Fullscreen.vue'; import Fullscreen from './src/Fullscreen.vue';
import Separator from './Separator.vue'; import Separator from './src/Separator.vue';
import SortNo from './SortNo.vue'; import SortNo from './src/SortNo.vue';
import StickyColumn from './StickyColumn.vue'; import Stripe from './src/Stripe.vue';
import AloneGorup from './AloneGroup.vue'; import StickyColumn from './src/StickyColumn.vue';
import AloneGorup from './src/AloneGroup.vue';
const $q = useQuasar(); const $q = useQuasar();
const tools = <GridTools>inject('tools'); const tools = <GridTools>inject('tools');

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

@ -18,7 +18,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { inject, ref, nextTick } 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');
const dynamicModel = ref({}); const dynamicModel = ref({});

2
io.sc.platform.core.frontend/src/platform/components/grid/extra/config/CheckboxSelection.vue → io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/CheckboxSelection.vue

@ -25,7 +25,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue'; import { inject } from 'vue';
import { GridTools } from '../../ts/index'; import { GridTools } from '../../../ts/index';
const tools = <GridTools>inject('tools'); const tools = <GridTools>inject('tools');

2
io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Dense.vue → io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/Dense.vue

@ -48,7 +48,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue'; import { inject } from 'vue';
import { GridTools } from '../../ts/index'; import { GridTools } from '../../../ts/index';
const tools = <GridTools>inject('tools'); const tools = <GridTools>inject('tools');
const denseChange = () => { const denseChange = () => {

2
io.sc.platform.core.frontend/src/platform/components/grid/extra/config/DisplayColumn.vue → io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/DisplayColumn.vue

@ -19,7 +19,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue'; import { inject } from 'vue';
import { Tools, NotifyManager } from '@/platform'; import { Tools, NotifyManager } from '@/platform';
import { GridTools, Constant } from '../../ts/index'; import { GridTools, Constant } from '../../../ts/index';
const modelValue = defineModel<boolean>(); const modelValue = defineModel<boolean>();
const tools = <GridTools>inject('tools'); const tools = <GridTools>inject('tools');

2
io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Fullscreen.vue → io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/Fullscreen.vue

@ -21,7 +21,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue'; import { inject } from 'vue';
import { GridTools } from '../../ts/index'; import { GridTools } from '../../../ts/index';
const tools = <GridTools>inject('tools'); const tools = <GridTools>inject('tools');
const props = defineProps({ const props = defineProps({

2
io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Separator.vue → io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/Separator.vue

@ -40,7 +40,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { inject, ref, watch } from 'vue'; import { inject, ref, watch } from 'vue';
import { GridTools } from '../../ts/index'; import { GridTools } from '../../../ts/index';
const tools = <GridTools>inject('tools'); const tools = <GridTools>inject('tools');

2
io.sc.platform.core.frontend/src/platform/components/grid/extra/config/SortNo.vue → io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/SortNo.vue

@ -25,7 +25,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue'; import { inject } from 'vue';
import { GridTools } from '../../ts/index'; import { GridTools } from '../../../ts/index';
const tools = <GridTools>inject('tools'); const tools = <GridTools>inject('tools');
const click = () => { const click = () => {

2
io.sc.platform.core.frontend/src/platform/components/grid/extra/config/StickyColumn.vue → io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/StickyColumn.vue

@ -26,7 +26,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { inject } from 'vue'; import { inject } from 'vue';
import { GridTools } from '../../ts/index'; import { GridTools } from '../../../ts/index';
const tools = <GridTools>inject('tools'); const tools = <GridTools>inject('tools');
const stickyOptions = [ const stickyOptions = [

39
io.sc.platform.core.frontend/src/platform/components/grid/extra/config/src/Stripe.vue

@ -0,0 +1,39 @@
<template>
<q-item :clickable="true" @click="click">
<q-item-section>
<q-item-label>斑马纹</q-item-label>
</q-item-section>
<q-item-section side>
<q-btn-group outline flat dense unelevated spread>
<q-btn
:color="tools.table.configStore.stripe ? 'primary' : ''"
:outline="tools.table.configStore.stripe ? false : true"
dense
label="显示"
unelevated
/>
<q-btn
:color="tools.table.configStore.stripe ? '' : 'primary'"
:outline="tools.table.configStore.stripe ? true : false"
dense
label="隐藏"
unelevated
/>
</q-btn-group>
</q-item-section>
</q-item>
</template>
<script setup lang="ts">
import { inject } from 'vue';
import { GridTools } from '../../../ts/index';
const tools = <GridTools>inject('tools');
const click = () => {
tools.table.configStore.stripe = !tools.table.configStore.stripe;
if (tools.table.configStore.stripe && !tools.opFM.stripeWatch) {
tools.opFM.setStripe();
} else {
tools.opFM.resetStripeStyle();
}
};
</script>

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

@ -146,6 +146,8 @@ export class GridTools {
stickyNum: props.stickyNum, stickyNum: props.stickyNum,
useCheckboxSelection: props.checkboxSelection, useCheckboxSelection: props.checkboxSelection,
showSortNo: props.sortNo, showSortNo: props.sortNo,
stripe: props.stripe ? true : false,
stripeColor: typeof props.stripe === 'boolean' ? '#fafafa' : props.stripe,
dense: dense, dense: dense,
denseToolbar: props.denseToolbar !== undefined ? props.denseToolbar : false, denseToolbar: props.denseToolbar !== undefined ? props.denseToolbar : false,
denseHeader: props.denseHeader !== undefined ? props.denseHeader : false, denseHeader: props.denseHeader !== undefined ? props.denseHeader : false,

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

@ -7,6 +7,11 @@ import { Constant, PropsType, TableType } from '../index';
* w-grid * w-grid
*/ */
export class Operator extends Base { export class Operator extends Base {
/**
*
*/
stripeWatch: boolean = false;
constructor(props: PropsType, table: TableType) { constructor(props: PropsType, table: TableType) {
super(props, table); super(props, table);
this.tableFullscreen = this.tableFullscreen.bind(this); this.tableFullscreen = this.tableFullscreen.bind(this);
@ -106,13 +111,24 @@ export class Operator extends Base {
if (trArr.length > 0) { if (trArr.length > 0) {
for (let i = 0; i < trArr.length; i++) { for (let i = 0; i < trArr.length; i++) {
if (i % 2 !== 0) { if (i % 2 !== 0) {
trArr[i].style['background-color'] = typeof this.props.stripe === 'boolean' ? '#fafafa' : this.props.stripe; trArr[i].style['background-color'] = this.table.configStore.stripeColor;
} else { } else {
trArr[i].style['background-color'] = ''; trArr[i].style['background-color'] = '';
} }
} }
} }
} }
/**
*
*/
private cleanStripeStyle() {
const tbodyDom = this.getTbodyDom();
for (let i = 0; i < tbodyDom.children.length; i++) {
if (!tbodyDom.children[i].classList.contains('groupTr')) {
tbodyDom.children[i].style['background-color'] = '';
}
}
}
private getTbodyDom() { private getTbodyDom() {
const tableElement = this.instance.getHtmlElement(); const tableElement = this.instance.getHtmlElement();
@ -136,8 +152,10 @@ export class Operator extends Base {
* *
*/ */
resetStripeStyle() { resetStripeStyle() {
if (!Tools.isEmpty(this.props.stripe) && this.props.stripe !== false) { if (this.table.configStore.stripe) {
this.setStripeStyle(this.getStripeTrArr()); this.setStripeStyle(this.getStripeTrArr());
} else {
this.cleanStripeStyle();
} }
} }
@ -145,7 +163,7 @@ export class Operator extends Base {
* *
*/ */
setStripe() { setStripe() {
if (!Tools.isEmpty(this.props.stripe) && this.props.stripe !== false) { if (this.table.configStore.stripe) {
const trArr = this.getStripeTrArr(); const trArr = this.getStripeTrArr();
// 初始化时设置一遍样式 // 初始化时设置一遍样式
this.setStripeStyle(trArr); this.setStripeStyle(trArr);
@ -154,7 +172,7 @@ export class Operator extends Base {
const observer = new MutationObserver((mutations) => { const observer = new MutationObserver((mutations) => {
const currentTrArr = this.getStripeTrArr(); const currentTrArr = this.getStripeTrArr();
const currentRowCount = currentTrArr.length; const currentRowCount = currentTrArr.length;
if (currentRowCount !== previousRowCount) { if (currentRowCount !== previousRowCount && this.table.configStore.stripe) {
previousRowCount = currentRowCount; previousRowCount = currentRowCount;
this.setStripeStyle(currentTrArr); this.setStripeStyle(currentTrArr);
} }
@ -165,6 +183,7 @@ export class Operator extends Base {
childList: true, // 监视子节点增删 childList: true, // 监视子节点增删
subtree: false, // 若tr嵌套在更深层级需启用 subtree: false, // 若tr嵌套在更深层级需启用
}); });
this.stripeWatch = true;
} }
} }

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

@ -26,6 +26,14 @@ export type ConfigPanelStoreType = {
* *
*/ */
showSortNo: boolean, showSortNo: boolean,
/**
*
*/
stripe: boolean,
/**
*
*/
stripeColor: string,
/** /**
* *
*/ */

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

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<br /> <!-- <br />
<br /> <br />
<br /> <br />
<br /> <br />
@ -155,128 +155,199 @@
<br /> <br />
<br /> <br />
<br /> <br />
<br /> <br /> -->
<w-form <w-form
ref="formRef" ref="formRef"
v-model="formModel"
:fields="[ :fields="[
{ {
name: 'test1', label: '分组1',
label: '文本框', icon: 'home',
type: 'w-text', iconAttrs: {
requiredIf: true, color: 'red',
rules: [ },
(value) => { color: 'auto',
console.info('value====', value); type: 'w-form-group',
return true; colSpan: 'full',
colsNum: 2,
fields: [
{
name: 'test1',
label: '文本框',
type: 'w-text',
requiredIf: true,
rules: [
(value) => {
return true;
},
],
},
{
name: 'test2',
label: '下拉框',
type: 'w-select',
requiredIf: true,
multiple: true,
options: [
{ label: '男', value: 1 },
{ label: '女', value: 0 },
],
onUpdateValue: (args) => {},
}, },
], ],
}, },
{ {
name: 'test2', label: '分组2',
label: '下拉框', icon: 'home',
type: 'w-select', color: 'auto',
requiredIf: true, iconAttrs: {
multiple: true, color: 'green',
options: [ },
{ label: '男', value: 1 }, type: 'w-form-group',
{ label: '女', value: 0 }, colSpan: 'full',
], fields: [
onUpdateValue: (args) => {}, {
}, name: 'test2.5',
{ label: '可输入可选择下拉框',
name: 'test2.5', type: 'w-input-select',
label: '可输入可选择下拉框', requiredIf: true,
type: 'w-input-select', defaultValue: 'wowowo',
requiredIf: true, options: [
defaultValue: 'wowowo', { label: 'java', value: '1' },
options: [ { label: 'python', value: '2' },
{ label: 'java', value: '1' }, ],
{ label: 'python', value: '2' }, },
{
name: 'test3',
label: '复选框',
type: 'w-checkbox',
onUpdateValue: (args) => {
console.info('value========', args);
},
},
], ],
}, },
{ {
name: 'test3', label: '分组3',
label: '复选框', icon: 'home',
type: 'w-checkbox', iconAttrs: {
onUpdateValue: (args) => { color: 'orange',
console.info('value========', args);
}, },
}, color: 'auto',
{ type: 'w-form-group',
name: 'test4',
label: '日期',
type: 'w-date',
clearable: true,
requiredIf: true,
},
{
name: 'test4.5',
label: '日期范围',
type: 'w-date-range',
clearable: true,
requiredIf: true,
},
{
name: 'test5',
label: '文件',
type: 'w-file',
requiredIf: true,
},
{
name: 'test6',
label: '数字',
type: 'w-number',
precision: 2,
colsFirst: true,
colSpan: 'full', colSpan: 'full',
requiredIf: (args) => { fields: [
return true; {
}, name: 'test4',
onUpdateValue: (args) => { label: '日期',
// console.info('value========', args); type: 'w-date',
}, clearable: true,
}, requiredIf: true,
{ },
name: 'test7', {
label: '密码', name: 'test4.5',
type: 'w-password', label: '日期范围',
requiredIf: true, type: 'w-date-range',
}, clearable: true,
{ requiredIf: true,
name: 'test8', },
label: '头像', ],
type: 'w-icon',
requiredIf: true,
},
{
name: 'test9',
label: '页面位置',
type: 'w-position',
requiredIf: true,
}, },
{ {
name: 'test10', label: '分组4',
label: '颜色', icon: 'home',
type: 'w-color-input', type: 'w-form-group',
requiredIf: true, colSpan: 'full',
fields: [
{
name: 'test5',
label: '文件',
type: 'w-file',
requiredIf: true,
},
{
name: 'test6',
label: '数字',
type: 'w-number',
precision: 2,
colsFirst: true,
colSpan: 'full',
requiredIf: (args) => {
return true;
},
onUpdateValue: (args) => {
// console.info('value========', args);
},
},
{
name: 'test7',
label: $t('password'),
type: 'w-password',
requiredIf: true,
},
{
name: 'test8',
label: '头像',
type: 'w-icon',
requiredIf: true,
},
],
}, },
{ {
name: 'test11', label: '分组5',
label: '颜色2', icon: 'home',
type: 'w-color-input-palette', color: 'auto',
requiredIf: true, iconAttrs: {
color: 'yellow',
},
type: 'w-form-group',
colSpan: 2,
colsNum: 2,
fields: [
{
name: 'test9',
label: '页面位置',
type: 'w-position',
requiredIf: true,
},
{
name: 'test10',
label: '颜色',
type: 'w-color-input',
requiredIf: true,
},
],
}, },
{ {
name: 'test12', label: '分组6',
label: '复选框组', icon: 'home',
requiredIf: true, color: 'auto',
type: 'w-checkbox-group', iconAttrs: {
simple: false, color: 'blue',
options: [ },
{ label: '唱歌', value: '1' }, type: 'w-form-group',
{ label: '跳舞', value: '2' }, colSpan: '2',
{ label: '运动', value: '3' }, colsNum: 2,
{ label: '看书', value: '4' }, fields: [
{
name: 'test11',
label: '颜色2',
type: 'w-color-input-palette',
requiredIf: true,
},
{
name: 'test12',
label: '复选框组',
requiredIf: true,
type: 'w-checkbox-group',
simple: false,
options: [
{ label: '唱歌', value: '1' },
{ label: '跳舞', value: '2' },
{ label: '运动', value: '3' },
{ label: '看书', value: '4' },
],
},
], ],
}, },
{ {
@ -331,26 +402,26 @@
type: 'w-user-select', type: 'w-user-select',
multiple: true, multiple: true,
requiredIf: true, requiredIf: true,
orgQueryCriteria: { // orgQueryCriteria: {
operator: 'or', // operator: 'or',
criteria: [ // criteria: [
{ // {
fieldName: 'id', // fieldName: 'id',
operator: 'equals', // operator: 'equals',
value: '2a6646d4-3bfa-480f-a3fc-5f298af96311', // value: 'bd49307c-32c1-47a0-9454-14f460f875ef',
}, // },
{ // {
fieldName: 'parent', // fieldName: 'parent',
operator: 'equals', // operator: 'equals',
value: '2a6646d4-3bfa-480f-a3fc-5f298af96311', // value: 'bd49307c-32c1-47a0-9454-14f460f875ef',
}, // },
], // ],
}, // },
roleQueryCriteria: { // roleQueryCriteria: {
fieldName: 'code', // fieldName: 'code',
operator: 'equals', // operator: 'equals',
value: 'KHJL', // value: 'KHJL',
}, // },
}, },
{ {
name: 'test15', name: 'test15',
@ -408,12 +479,15 @@
> >
</w-form> </w-form>
<q-btn label="提交" @click="submit"></q-btn>&nbsp; <q-btn label="重置" @click="reset"></q-btn>&nbsp; <q-btn label="提交" @click="submit"></q-btn>&nbsp; <q-btn label="重置" @click="reset"></q-btn>&nbsp;
<q-btn label="初始化值" @click="setValue"></q-btn>&nbsp; <q-btn label="初始化值" @click="setValue"></q-btn>&nbsp;<q-btn label="api测试" @click="apiTest"></q-btn> &nbsp;<q-btn
label="清除错误"
@click="resetValidate"
></q-btn>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed } from 'vue'; import { ref, reactive, computed } from 'vue';
import { Environment, Formater } from '@/platform'; import { Environment, Formater, $t } from '@/platform';
const mode = ref('criteria'); const mode = ref('criteria');
const booleanModelValue = ref(false); const booleanModelValue = ref(false);
@ -441,9 +515,11 @@ const objectModelValue = ref({
const queryBuilderRef = ref(); const queryBuilderRef = ref();
const formRef = ref(); const formRef = ref();
const formModel = reactive({});
const submit = async () => { const submit = async () => {
console.info('表单数据:', formRef.value.getData()); formModel['test6'] = 888;
console.info('表单数据====', formRef.value.getData());
const validateResult = await formRef.value.validate(); const validateResult = await formRef.value.validate();
console.info('表单验证结果:', validateResult); console.info('表单验证结果:', validateResult);
}; };
@ -459,7 +535,44 @@ const setValue = () => {
ky: true, ky: true,
sex: 1, sex: 1,
addr: '上海市', addr: '上海市',
test6: 455,
}; };
formRef.value.setData(data); formRef.value.setData(data);
}; };
const apiTest = () => {
// formRef.value.setStatus('dddd');
// const status = formRef.value.getStatus();
// console.info('status=====', status);
// formRef.value.setFieldValue('test6', 88888);
// const fieldValue = formRef.value.getFieldValue('test6');
// console.info('fieldValue====', fieldValue);
// const colsNum = formRef.value.getColsNum();
// console.info('colsNum====', colsNum);
// const fieldRef = formRef.value.getFieldComponent('test6');
// console.info('fieldRef======', fieldRef);
const fields = formRef.value.getFields();
fields['test2'].options = [{ label: 'ddd', value: 3 }];
console.info('fields=========', fields);
// const errors = [
// {
// fieldName: 'test6',
// errorMessageI18nKey: 'password',
// },
// {
// fieldName: 'test1',
// errorMessage: '',
// },
// ];
// formRef.value.setValidationErrors(errors);
// formRef.value.resetValidation();
};
const resetValidate = () => {
formRef.value.resetValidation();
};
</script> </script>

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

@ -1,54 +1,227 @@
<template> <template>
<w-grid <q-splitter :model-value="60" class="w-full" style="height: 100%">
title="用户列表" <template #before>
:checkbox-selection="false" <div class="pr-1" style="height: 100%">
:data-url="Environment.apiContextPath('/api/system/user')" <w-grid
:pageable="false" ref="roleGridRef"
:toolbar-actions="[ :title="$t('system.role.grid.title')"
'query', :config-button="true"
'reset', selection="multiple"
'separator', db-click-operation="edit"
'add', :checkbox-selection="true"
'edit', :data-url="Environment.apiContextPath('/api/system/role')"
'remove', :pagination="{
'expand', sortBy: 'name',
[ descending: false,
{ }"
name: 'ddd', :query-form-cols-num="3"
icon: 'add', :query-form-fields="[
label: $t('loginName'), { name: 'code', label: $t('code'), type: 'w-text' },
}, { name: 'name', label: $t('name'), type: 'w-text' },
'addTop', { name: 'enable', label: $t('isEnable'), type: 'w-select', options: Options.yesNo() },
['addChild', 'cellEdit', 'clone'], ]"
], :toolbar-configure="{ noIcon: false }"
]" :toolbar-actions="['query', 'refresh', 'separator', 'add', 'clone', 'edit', 'remove', 'separator', 'view', 'separator', 'export']"
:advanced-query="true" :columns="[
:stripe="true" { width: 200, name: 'code', label: $t('code') },
:query-form-fields="[ { width: '100%', name: 'name', label: $t('name') },
{ name: 'defaultOrgId', label: '所属机构', type: 'w-org-select', multiple: true }, { width: 70, name: 'enable', label: $t('status'), align: 'center', format: Formater.enableTag() },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), type: 'w-date-range' }, {
{ name: 'loginName', label: $t('loginName'), type: 'w-text', showIf: true }, width: 200,
{ name: 'userName', label: $t('userName'), type: 'w-text' }, name: 'corporationCode',
]" label: $t('corporation'),
:columns="[ showIf: SessionManager.isPrimaryCorporation(),
{ name: 'loginName', label: '很长很长的表头可绕地球一圈', width: 100, title: 'dfdf' }, format: (value) => {
{ name: 'userName', label: $t('userName') }, return corporationMapRef[value];
{ name: 'lastModifier', label: '最后修改人' }, },
{ },
name: 'lastModifyDate', ]"
label: $t('lastModifyDate'), :editor="{
}, dialog: {
]" width: '600px',
:editor="{ },
form: { form: {
fields: [ colsNum: 1,
{ name: 'loginName', label: $t('loginName'), type: 'w-text' }, fields: [
{ name: 'userName', label: $t('userName'), type: 'w-text' }, { name: 'code', label: $t('code'), type: 'w-text', requiredIf: true },
], { name: 'name', label: $t('name'), type: 'w-text', requiredIf: true },
}, { name: 'description', label: $t('description'), type: 'w-textarea', rows: 1 },
}" {
></w-grid> name: 'corporationCode',
label: $t('corporation'),
type: 'w-select',
options: corporationOptionRef,
showIf: () => {
return SessionManager.isPrimaryCorporation();
},
},
{ name: 'enable', label: $t('enable'), type: 'w-checkbox', defaultValue: true },
],
},
}"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'code', label: $t('code') },
{ name: 'name', label: $t('name') },
{ name: 'description', label: $t('description') },
{ name: 'enable', label: $t('enable'), format: Formater.none() },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() },
{ name: 'corporationCode', label: $t('corporationCode') },
],
},
}"
@row-click="
(args) => {
refreshRelationshipComponents(args.row.id);
}
"
@before-request-data="
() => {
menuTreeGridRef?.clear();
userGridRef?.clear();
}
"
>
</w-grid>
</div>
</template>
<template #after>
<div class="pl-1" style="height: 100%">
<q-tabs v-model="selectedTabRef" inline-label align="left" :breakpoint="0" no-caps>
<q-tab name="menu" icon="bi-menu-app" :label="$t('menu')" />
<q-tab name="user" icon="bi-person" :label="$t('user')" />
</q-tabs>
<q-tab-panels v-model="selectedTabRef" animated swipeable keep-alive style="height: calc(100% - 48px)">
<q-tab-panel name="menu" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<SelectMenuTreeGrid
ref="menuTreeGridRef"
:fetch-data-url="Environment.apiContextPath('/api/system/menu/listAllMenusWithSelectedStatusByRole')"
foreign-key="roleId"
:foreign-value="currentSelectedRoleId"
@update="update"
></SelectMenuTreeGrid>
</q-tab-panel>
<q-tab-panel name="user" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px">
<SelectUserGrid
ref="userGridRef"
:fetch-data-url="Environment.apiContextPath('/api/system/user/queryUsersByRole')"
:fetch-other-data-url="Environment.apiContextPath('/api/system/user/queryOtherUsersByRole')"
foreign-key="roleId"
:foreign-value="currentSelectedRoleId"
@select-in="selectIn"
@select-out="selectOut"
@select-all-in="selectAllIn"
@select-all-out="selectAllOut"
>
</SelectUserGrid>
</q-tab-panel>
</q-tab-panels>
</div>
</template>
</q-splitter>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Environment, Formater, $t } from '@/platform'; import { ref, onActivated } from 'vue';
import { Environment, axios, Options, Formater, SessionManager } from '@/platform';
import SelectUserGrid from './SelectUserGrid.vue';
import SelectMenuTreeGrid from './SelectMenuTreeGrid.vue';
const corporationMapRef = ref({});
const corporationOptionRef = ref([]);
const roleGridRef = ref();
const userGridRef = ref();
const menuTreeGridRef = ref();
const selectedTabRef = ref('menu');
const currentSelectedRoleId = ref('');
const refreshRelationshipComponents = (id) => {
currentSelectedRoleId.value = id;
menuTreeGridRef.value?.refresh();
userGridRef.value?.refresh();
};
const selectIn = (ids: string[], gridComponent, dialogComponent) => {
axios
.post(Environment.apiContextPath('/api/system/role/addUsers'), {
one: roleGridRef.value.getSelectedRows()[0].id,
many: ids,
})
.then(() => {
gridComponent?.refresh();
dialogComponent?.close();
});
};
const selectOut = (ids, gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/role/removeUsers'), {
one: roleGridRef.value.getSelectedRows()[0].id,
many: ids,
})
.then(() => {
gridComponent?.refresh();
});
};
const selectAllIn = (gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/role/addAllUsers'), {
one: roleGridRef.value.getSelectedRows()[0].id,
many: [],
})
.then(() => {
gridComponent?.refresh();
});
};
const selectAllOut = (gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/role/removeAllUsers'), {
one: roleGridRef.value.getSelectedRows()[0].id,
many: [],
})
.then(() => {
gridComponent?.refresh();
});
};
const update = (ids, gridComponent) => {
axios
.post(Environment.apiContextPath('/api/system/role/updateMenus'), {
one: roleGridRef.value.getSelectedRows()[0].id,
many: ids,
})
.then(() => {
gridComponent.refresh();
});
};
onActivated(() => {
menuTreeGridRef.value?.refresh();
userGridRef.value?.refresh();
if (SessionManager.isPrimaryCorporation()) {
axios.get(Environment.apiContextPath('/api/system/corporation?pageable=false')).then((response) => {
const options = [];
const corporationMap = {};
if (response.data?.content && response.data?.content.length > 0) {
for (const item of response.data.content) {
options.push({ label: item.name, value: item.code });
corporationMap[item.code] = item.name;
}
}
corporationOptionRef.value = options;
corporationMapRef.value = corporationMap;
});
}
});
</script> </script>

125
io.sc.platform.core.frontend/src/views/likm/SelectMenuTreeGrid.vue

@ -0,0 +1,125 @@
<template>
<w-grid
ref="treeGridRef"
:title="$t('system.shared.selectMenu.grid.title')"
hide-bottom
:config-button="false"
:tree="true"
selection="multiple"
:checkbox-selection="true"
tree-tick-strategy="strict"
:tree-icon="
(row) => {
if (row.type === 'SEPARATOR') {
return { name: 'bi-dash-lg' };
} else if (row.type === 'ROUTE_ACTION') {
return { name: 'sym_o_crop_16_9' };
} else {
return { name: row.icon };
}
}
"
:fetch-data-url="fetchDataUrl + '?' + foreignKey + '=' + foreignValue"
:auto-fetch-data="true"
:pageable="false"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'refresh',
'separator',
{
extend: 'expand',
enableIf: () => {
return foreignValue && treeGridRef?.getRows()?.length > 0;
},
},
{
name: 'save',
label: $t('save'),
icon: 'bi-floppy',
enableIf: () => {
return foreignValue && treeGridRef?.getRows()?.length > 0;
},
click: (arg) => {
DialogManager.confirm($t('system.shared.selectMenu.grid.toolbar.save.tip'), () => {
const ids = Tools.extractProperties(treeGridRef.getTickedRows(true), 'id');
emit('update', ids, treeGridRef);
});
},
},
'separator',
'view',
]"
:columns="[
{
width: '100%',
name: 'titleI18nKey',
label: $t('name'),
sortable: false,
format: (value, row) => {
if (row.type === 'SEPARATOR') {
return `<hr style='width:100px'/>`;
} else if (row.type === 'ROUTE_ACTION') {
return $t(row.i18nKey);
} else {
return $t(value);
}
},
},
{ width: 70, name: 'enable', label: $t('status'), format: Formater.enableTag(), sortable: false },
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'type', label: $t('type') },
{ name: 'name', label: $t('name') },
{ name: 'titleI18nKey', label: $t('titleI18nKey') },
{ name: 'icon', label: $t('icon') },
{ name: 'enable', label: $t('enable') },
{ name: 'order', label: $t('order') },
{ name: 'javaScript', label: $t('javaScript') },
{ name: 'url', label: $t('url') },
{ name: 'urlOpenType', label: $t('urlOpenType') },
{ name: 'routeName', label: $t('routeName') },
{ name: 'routeQuery', label: $t('routeQuery') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate') },
{ name: 'corporationCode', label: $t('corporationCode') },
],
},
}"
></w-grid>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { DialogManager, Formater, Tools } from '@/platform';
defineProps({
fetchDataUrl: { type: String, default: '' },
foreignKey: { type: String, default: '' },
foreignValue: { type: String, default: '' },
});
const emit = defineEmits<{
(e: 'update', ids: string[], gridComponent: any): void;
}>();
const treeGridRef = ref();
const refresh = () => {
treeGridRef.value?.refresh();
};
const clear = () => {
treeGridRef.value?.setLocalData([]);
};
defineExpose({
refresh,
clear,
});
</script>

66
io.sc.platform.core.frontend/src/views/likm/SelectUserDialog.vue

@ -1,7 +1,7 @@
<template> <template>
<w-dialog <w-dialog
ref="dialogRef" ref="dialogRef"
:title="$t('system.role.selectUser.dialog.title')" :title="$t('system.shared.selectUser.dialog.title')"
width="800px" width="800px"
height="500px" height="500px"
:can-maximize="false" :can-maximize="false"
@ -9,58 +9,53 @@
{ {
label: $t('confirm'), label: $t('confirm'),
click: () => { click: () => {
const userIds = Tools.extractProperties(gridRef.getSelectedRows(), 'id'); const ids = Tools.extractProperties(gridRef.getSelectedRows(), 'id');
axios emit('afterSelected', ids, dialogRef);
.post(Environment.apiContextPath('/api/system/role/addUsers'), {
one: roleId,
many: userIds,
})
.then((response) => {
userGridRef?.refresh();
close();
});
}, },
}, },
]" ]"
> >
<div class="px-2"> <div class="px-2" style="height: 100%">
<w-grid <w-grid
ref="gridRef" ref="gridRef"
:title="$t('system.user.grid.title')" :title="$t('system.shared.selectUser.dialog.grid.title')"
selection="multiple" selection="multiple"
:full-screen-button="false" :full-screen-button="false"
:toolbar-configure="{ noIcon: false }" :toolbar-configure="{ noIcon: false }"
:toolbar-actions="['query', 'refresh']" :toolbar-actions="['query', 'refresh']"
:query-form-fields="[ :query-form-fields="[
{ name: 'loginName', label: $t('loginName'), type: 'text' }, { name: 'loginName', label: $t('loginName'), type: 'w-text' },
{ name: 'userName', label: $t('userName'), type: 'text' }, { name: 'userName', label: $t('userName'), type: 'w-text' },
{ {
name: 'enable', name: 'enable',
label: $t('enable'), label: $t('enable'),
type: 'select', type: 'w-select',
options: Options.yesNo(), options: Options.yesNo(),
queryOperator: 'equals', queryOperator: 'equals',
}, },
{ {
name: 'dataComeFrom', name: 'dataComeFrom',
label: $t('dataComeFrom'), label: $t('dataComeFrom'),
type: 'select', type: 'w-select',
options: Options.enum(DataComeFromEnum), options: Options.enum(DataComeFromEnum),
queryOperator: 'equals', queryOperator: 'equals',
}, },
]" ]"
:auto-fetch-data="false" :auto-fetch-data="false"
:fetch-data-url="Environment.apiContextPath('/api/system/user/queryOtherUsersByRole?roleId=' + roleId)" :fetch-data-url="fetchDataUrl + '?' + foreignKey + '=' + foreignValue"
:columns="[ :columns="[
{ name: 'loginName', label: $t('loginName') }, { width: 100, name: 'loginName', label: $t('loginName') },
{ name: 'userName', label: $t('userName') }, { width: 100, name: 'userName', label: $t('userName') },
{ {
name: 'status', name: 'status',
label: t('status'), label: $t('status'),
format: Formater.enableTag(), format: (value, row) => {
return {
componentType: UserStatusTag,
attrs: row,
};
},
}, },
{ name: 'lastModifier', label: t('lastModifier') },
{ name: 'lastModifyDate', label: t('lastModifyDate'), format: Formater.dateOnly() },
]" ]"
></w-grid> ></w-grid>
</div> </div>
@ -69,17 +64,28 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, nextTick } from 'vue'; import { ref, nextTick } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { axios, Environment, Tools, EnumTools, Options, Formater } from '@/platform'; import { Environment, Tools, EnumTools, Options, Formater } from '@/platform';
import UserStatusTag from './UserStatusTag.vue';
const props = defineProps({
opener: { type: Object, default: undefined },
fetchDataUrl: { type: String, default: '' },
foreignKey: { type: String, default: '' },
foreignValue: { type: String, default: '' },
});
const emit = defineEmits<{
(e: 'afterSelected', ids: string[], dialogComponent: any): void;
}>();
const { t } = useI18n(); const { t } = useI18n();
const dialogRef = ref(); const dialogRef = ref();
const gridRef = ref(); const gridRef = ref();
let roleId, userGridRef; const foreignKeyRef = ref();
const open = (param: object) => {
roleId = param.roleId;
userGridRef = param.userGrid;
const open = (foreignKey: string) => {
foreignKeyRef.value = foreignKey;
dialogRef.value.show(); dialogRef.value.show();
nextTick(() => { nextTick(() => {

165
io.sc.platform.core.frontend/src/views/likm/SelectUserGrid.vue

@ -0,0 +1,165 @@
<template>
<w-grid
ref="gridRef"
:title="$t('system.shared.selectUser.grid.title')"
:config-button="false"
selection="multiple"
:checkbox-selection="true"
:fetch-data-url="fetchDataUrl + '?' + foreignKey + '=' + foreignValue"
:auto-fetch-data="true"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'refresh',
'separator',
{
name: 'selectIn',
label: $t('system.shared.selectUser.grid.toolbar.selectIn'),
icon: 'bi-download',
enableIf: () => {
return foreignValue ? true : false;
},
click: () => {
dialogRef.open(foreignValue);
},
},
{
name: 'selectOut',
label: $t('system.shared.selectUser.grid.toolbar.selectOut'),
icon: 'bi-upload',
enableIf: () => {
return foreignValue && gridRef?.getSelectedRows()?.length > 0;
},
click: (arg) => {
const ids = Tools.extractProperties(arg.selecteds, 'id');
DialogManager.confirm($t('system.shared.selectUser.grid.toolbar.selectOut.tip'), () => {
emit('selectOut', ids, gridRef);
});
},
},
'separator',
{
name: 'selectAllIn',
label: $t('system.shared.selectUser.grid.toolbar.selectAllIn'),
icon: 'bi-box-arrow-in-down',
enableIf: () => {
return foreignValue ? true : false;
},
click: () => {
DialogManager.confirm($t('system.shared.selectUser.grid.toolbar.selectAllIn.tip'), () => {
emit('selectAllIn', gridRef);
});
},
},
{
name: 'selectAllOut',
label: $t('system.shared.selectUser.grid.toolbar.selectAllOut'),
icon: 'bi-box-arrow-up',
enableIf: () => {
return foreignValue && gridRef?.getRows()?.length > 0;
},
click: () => {
DialogManager.confirm($t('system.shared.selectUser.grid.toolbar.selectAllOut.tip'), () => {
emit('selectAllOut', gridRef);
});
},
},
'separator',
'view',
]"
:columns="[
{ width: 150, name: 'loginName', label: $t('loginName') },
{ width: '100%', name: 'userName', label: $t('userName') },
{
width: 150,
name: 'enable',
label: $t('status'),
format: (value, row) => {
return {
componentType: UserStatusTag,
attrs: row,
};
},
},
]"
:viewer="{
panel: {
columnNum: 1,
fields: [
{ name: 'id', label: $t('id') },
{ name: 'loginName', label: $t('loginName') },
{ name: 'userName', label: $t('userName') },
{ name: 'description', label: $t('description') },
{
name: 'enable',
label: $t('enable'),
format: (value) => {
return value;
},
},
{ name: 'accountExpired', label: $t('accountExpired') },
{ name: 'accountLocked', label: $t('accountLocked') },
{ name: 'credentialsExpired', label: $t('credentialsExpired') },
{ name: 'email', label: $t('email') },
{ name: 'phone', label: $t('phone') },
{ name: 'mobile', label: $t('mobile') },
{ name: 'weixin', label: $t('weixin') },
{ name: 'qq', label: $t('qq') },
{ name: 'dataComeFrom', label: $t('dataComeFrom') },
{ name: 'creator', label: $t('creator') },
{ name: 'createDate', label: $t('createDate') },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate') },
{ name: 'corporationCode', label: $t('corporationCode') },
],
},
}"
></w-grid>
<SelectUserDialog
ref="dialogRef"
:opener="gridRef"
:fetch-data-url="fetchOtherDataUrl"
:foreign-key="foreignKey"
:foreign-value="foreignValue"
@after-selected="
(ids: string[]) => {
emit('selectIn', ids, gridRef, dialogRef);
}
"
></SelectUserDialog>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { DialogManager, Tools } from '@/platform';
import SelectUserDialog from './SelectUserDialog.vue';
import UserStatusTag from './UserStatusTag.vue';
defineProps({
fetchDataUrl: { type: String, default: '' },
fetchOtherDataUrl: { type: String, default: '' },
foreignKey: { type: String, default: '' },
foreignValue: { type: String, default: '' },
});
const emit = defineEmits<{
(e: 'selectIn', ids: string[], gridComponent: any, dialogComponent: any): void;
(e: 'selectOut', ids: string[], gridComponent: any): void;
(e: 'selectAllIn', gridComponent: any): void;
(e: 'selectAllOut', gridComponent: any): void;
}>();
const gridRef = ref();
const dialogRef = ref();
const refresh = () => {
gridRef.value?.refresh();
};
const clear = () => {
gridRef.value?.setLocalData([]);
};
defineExpose({
refresh,
clear,
});
</script>

1
io.sc.platform.core.frontend/src/views/likm/TreeGrid.vue

@ -7,7 +7,6 @@
:fetch-data-url="Environment.apiContextPath('api/system/menu/allMenus')" :fetch-data-url="Environment.apiContextPath('api/system/menu/allMenus')"
:tree="true" :tree="true"
db-click-operation="expand" db-click-operation="expand"
dnd-mode="server"
:columns="[ :columns="[
{ {
name: 'name', name: 'name',

170
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/service/impl/OrgSearchServiceImpl.java

@ -1,10 +1,8 @@
package io.sc.platform.lcdp.frontend.component.service.impl; package io.sc.platform.lcdp.frontend.component.service.impl;
import io.sc.platform.lcdp.frontend.component.service.OrgSearchService; import io.sc.platform.lcdp.frontend.component.service.OrgSearchService;
import io.sc.platform.lcdp.frontend.component.service.UserSearchService; import io.sc.platform.lcdp.frontend.component.support.CriteriaHandler;
import io.sc.platform.lcdp.frontend.component.support.UserSearchConstant; import io.sc.platform.lcdp.frontend.component.support.SearchConstant;
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.QueryParameter;
import io.sc.platform.orm.service.support.criteria.Criteria; import io.sc.platform.orm.service.support.criteria.Criteria;
import io.sc.platform.orm.service.support.criteria.impl.Equals; import io.sc.platform.orm.service.support.criteria.impl.Equals;
@ -12,20 +10,16 @@ import io.sc.platform.orm.service.support.criteria.impl.InSet;
import io.sc.platform.orm.util.EntityVoUtil; import io.sc.platform.orm.util.EntityVoUtil;
import io.sc.platform.security.util.SecurityUtil; import io.sc.platform.security.util.SecurityUtil;
import io.sc.platform.system.api.org.OrgVo; import io.sc.platform.system.api.org.OrgVo;
import io.sc.platform.system.department.jpa.entity.DepartmentEntity;
import io.sc.platform.system.org.jpa.entity.OrgEntity; import io.sc.platform.system.org.jpa.entity.OrgEntity;
import io.sc.platform.system.org.service.OrgService; 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 io.sc.platform.util.StringUtil; import io.sc.platform.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@ -33,115 +27,129 @@ public class OrgSearchServiceImpl implements OrgSearchService {
@Autowired @Autowired
private OrgService orgService; private OrgService orgService;
@Autowired
private UserSearchServiceImpl userSearchServiceImpl;
@Override @Override
public Page<OrgVo> queryOrg(QueryParameter queryParameter) throws Exception { public Page<OrgVo> queryOrg(QueryParameter queryParameter) throws Exception {
if (existCondition(queryParameter)) { QueryParameter searchParameter = criteriaHandler(queryParameter);
return existConditionQuery(queryParameter); Page<OrgEntity> page = orgService.query(searchParameter);
} else { return EntityVoUtil.toVo(page);
return noConditionQuery(queryParameter);
}
} }
private Page<OrgVo> existConditionQuery(QueryParameter queryParameter) throws Exception { private QueryParameter criteriaHandler(QueryParameter queryParameter) throws Exception {
List<Criteria> orgCriteriaList = new ArrayList<>(); if (queryParameter.existsCriteria()) {
/** List<Criteria> orgCriteriaList = new ArrayList<>();
* 前端已将 queryCriteria使用用户默认机构查询等重新使用 and 操作进行组装后端需提取特定 criteria 出来进行处理 /**
* 只要存在查询条件最外层一定是 { fieldName: null, operator: 'and', criteria: [...] } * 前端已将 queryCriteria使用用户默认机构查询等重新使用 and 操作进行组装后端需提取特定 criteria 出来进行处理
* 所以最外层无需处理直接处理最外层的 criteria 数组 * 只要存在查询条件最外层一定是 { fieldName: null, operator: 'and', criteria: [...] }
*/ * 所以最外层无需处理直接处理最外层的 criteria 数组
List<Criteria> criterias = queryParameter.getCriterias().get(0).getCriteria(); */
for (Criteria criteria: criterias) { List<Criteria> criterias = queryParameter.getCriterias().get(0).getCriteria();
if (criteria.getFieldName()!=null) { for (Criteria criteria: criterias) {
Boolean isOrgCriteria = Arrays.stream(UserSearchConstant.ORG_CRITERIA_FIELD_NAMES).anyMatch(criteria.getFieldName()::equals); if (criteria.getFieldName()!=null) {
if (!isOrgCriteria) { Boolean isOrgCriteria = Arrays.stream(SearchConstant.ORG_CRITERIA_FIELD_NAMES).anyMatch(criteria.getFieldName()::equals);
orgCriteriaList.add(criteria); if (!isOrgCriteria) {
orgCriteriaList.add(criteria);
} else {
// 当 criteria 中的 fieldName 为特定字段名时,进行特殊处理
List<String> orgIds = findOrgIds(criteria.getFieldName(), criteria);
if (orgIds != null) {
// 不为 null 添加机构过滤逻辑。
CriteriaHandler.criteriaListAddInSet(orgCriteriaList, "id", orgIds);
}
}
} else { } else {
// 当 criteria 中的 fieldName 为特定字段名时,进行特殊处理 orgCriteriaList.add(criteria);
orgHandler(criteria.getFieldName(), criteria, orgCriteriaList);
} }
} else {
orgCriteriaList.add(criteria);
} }
return CriteriaHandler.buildQueryParameter(queryParameter, orgCriteriaList);
} else {
return queryParameter;
} }
QueryParameter newQueryParameter = userSearchServiceImpl.rebuildQueryParameter(queryParameter, orgCriteriaList);
Page<OrgEntity> page = orgService.query(newQueryParameter);
return EntityVoUtil.toVo(page);
}
private Page<OrgVo> noConditionQuery(QueryParameter queryParameter) throws Exception {
Page<OrgEntity> page = orgService.query(queryParameter);
return EntityVoUtil.toVo(page);
}
private Boolean existCondition(QueryParameter queryParameter) {
List<Criteria> list = queryParameter.getCriterias();
if (list !=null && list.size() > 0 && list.get(0).getCriteria() != null && list.get(0).getCriteria().size() > 0) {
return true;
}
return false;
} }
private void orgHandler(String fieldName, Criteria criteria, List<Criteria> orgCriteriaList) throws Exception { public List<String> findOrgIds(String fieldName, Criteria criteria) throws Exception {
List<String> orgIds = new ArrayList<>();
switch(fieldName) { switch(fieldName) {
case UserSearchConstant.FN_FINDBYORGIDWITHSUB: case SearchConstant.FN_ORGCRITERIA:
findByOrgIdWithSubHandler((InSet) criteria, orgCriteriaList); orgIds = orgCriteriaHandler((Equals) criteria);
break;
case SearchConstant.FN_FINDBYORGIDWITHSUB:
orgIds = findByOrgIdHandler((InSet) criteria, true);
break; break;
case UserSearchConstant.FN_FINDBYORGID: case SearchConstant.FN_FINDBYORGID:
findByOrgIdHandler((InSet) criteria, orgCriteriaList); orgIds = findByOrgIdHandler((InSet) criteria, false);
break; break;
case UserSearchConstant.FN_FINDBYDEFAULTORGWITHSUB: case SearchConstant.FN_FINDBYDEFAULTORGWITHSUB:
findByDefaultOrg(orgCriteriaList, true); orgIds = findByDefaultOrg(true);
break; break;
case UserSearchConstant.FN_FINDBYDEFAULTORG: case SearchConstant.FN_FINDBYDEFAULTORG:
findByDefaultOrg(orgCriteriaList, false); orgIds = findByDefaultOrg(false);
break; break;
} }
return orgIds;
} }
private void findByOrgIdWithSubHandler(InSet criteria, List<Criteria> orgCriteriaList) { private List<String> orgCriteriaHandler(Equals criteria) throws Exception {
QueryParameter queryParameter = new QueryParameter();
queryParameter.setCriteria(criteria.getValue());
queryParameter.setPageable(false);
List<OrgEntity> orgList = orgService.list(queryParameter);
List<String> orgIds = new ArrayList<>();
if (orgList!=null && orgList.size() > 0) {
orgIds = orgList.stream().map(OrgEntity::getId).collect(Collectors.toList());
}
return orgIds;
}
private List<String> findByOrgIdHandler(InSet criteria, Boolean withSub) {
String[] ids = criteria.getValue(); String[] ids = criteria.getValue();
List<OrgEntity> list = orgService.getRepository().findAllById(Arrays.asList(ids)); List<OrgEntity> list = orgService.getRepository().findAllById(Arrays.asList(ids));
List<String> orgIds = new ArrayList<>(); List<String> orgIds = new ArrayList<>();
if (list!=null && list.size()>0) { if (list!=null && list.size()>0) {
for(OrgEntity org: list) { for(OrgEntity org: list) {
orgIds.add(org.getId()); orgIds.add(org.getId());
userSearchServiceImpl.recursiveGetChildren(org, orgIds); if (withSub) {
recursiveGetChildren(org, orgIds);
}
} }
} }
userSearchServiceImpl.criteriaListAddInSet(orgCriteriaList, "id", orgIds); return orgIds;
}
private void findByOrgIdHandler(InSet criteria, List<Criteria> orgCriteriaList) {
String[] ids = criteria.getValue();
List<OrgEntity> list = orgService.getRepository().findAllById(Arrays.asList(ids));
List<String> orgIds = list.stream().map(OrgEntity::getId).collect(Collectors.toList());
userSearchServiceImpl.criteriaListAddInSet(orgCriteriaList, "id", orgIds);
} }
private void findByDefaultOrg(List<Criteria> orgCriteriaList, Boolean withSub) { private List<String> findByDefaultOrg(Boolean withSub) {
String orgId = SecurityUtil.getDefaultOrgId(); String orgId = SecurityUtil.getDefaultOrgId();
List<String> orgIds = new ArrayList<>();
if (!StringUtil.isNullOrEmpty(orgId)) { if (!StringUtil.isNullOrEmpty(orgId)) {
OrgEntity org = orgService.findById(orgId); OrgEntity org = orgService.findById(orgId);
if (org!=null) { if (org!=null) {
// TODO 机构是否为总行机构逻辑还需完善,目前判定 parent 为空为总行机构,多法人情况下该逻辑可能不适用。
if (org.getParent() != null) { if (org.getParent() != null) {
// 非总行机构添加查询条件,总行机构不用添加,查所有用户 // 非总行机构
List<String> orgIds = new ArrayList<>();
orgIds.add(org.getId()); orgIds.add(org.getId());
if (withSub) { if (withSub) {
userSearchServiceImpl.recursiveGetChildren(org, orgIds); recursiveGetChildren(org, orgIds);
} }
userSearchServiceImpl.criteriaListAddInSet(orgCriteriaList, "id", orgIds); } else {
// TODO 机构是否为总行机构逻辑还需完善,目前判定 parent 为空为总行机构,多法人情况下该逻辑可能不适用。
// 总行机构返回 null 表示不添加机构过滤逻辑。
return null;
} }
} else {
// 默认机构为空不允许查询用户
userSearchServiceImpl.criteriaListAddInSet(orgCriteriaList, "id", null);
} }
} else { }
userSearchServiceImpl.criteriaListAddInSet(orgCriteriaList, "id", null); return orgIds;
}
/**
* 递归获取其所有孩子的ID
* @param org 机构
* @param ids 孩子机构ID集合
*/
private void recursiveGetChildren(OrgEntity org, List<String> ids) {
List<OrgEntity> children =org.getChildren();
if(children!=null && !children.isEmpty()){
for(OrgEntity child : children){
ids.add(child.getId());
recursiveGetChildren(child,ids);
}
} }
} }
} }

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

@ -1,26 +1,19 @@
package io.sc.platform.lcdp.frontend.component.service.impl; 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.service.UserSearchService;
import io.sc.platform.lcdp.frontend.component.support.UserSearchConstant; import io.sc.platform.lcdp.frontend.component.support.CriteriaHandler;
import io.sc.platform.lcdp.frontend.component.support.UserSearchQueryParameter; import io.sc.platform.lcdp.frontend.component.support.SearchConstant;
import io.sc.platform.lcdp.frontend.component.vo.UserSearchVo; 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.QueryParameter;
import io.sc.platform.orm.service.support.criteria.Criteria; import io.sc.platform.orm.service.support.criteria.Criteria;
import io.sc.platform.orm.service.support.criteria.impl.Equals; import io.sc.platform.orm.service.support.criteria.impl.Equals;
import io.sc.platform.orm.service.support.criteria.impl.InSet;
import io.sc.platform.orm.util.EntityVoUtil;
import io.sc.platform.security.util.SecurityUtil;
import io.sc.platform.system.department.jpa.entity.DepartmentEntity; import io.sc.platform.system.department.jpa.entity.DepartmentEntity;
import io.sc.platform.system.menu.jpa.entity.MenuEntity;
import io.sc.platform.system.org.jpa.entity.OrgEntity; import io.sc.platform.system.org.jpa.entity.OrgEntity;
import io.sc.platform.system.org.service.OrgService; import io.sc.platform.system.org.service.OrgService;
import io.sc.platform.system.role.jpa.entity.RoleEntity; import io.sc.platform.system.role.jpa.entity.RoleEntity;
import io.sc.platform.system.role.service.RoleService; import io.sc.platform.system.role.service.RoleService;
import io.sc.platform.system.user.jpa.entity.UserEntity; import io.sc.platform.system.user.jpa.entity.UserEntity;
import io.sc.platform.system.user.service.UserService; import io.sc.platform.system.user.service.UserService;
import io.sc.platform.util.StringUtil;
import org.h2.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
@ -38,175 +31,63 @@ public class UserSearchServiceImpl implements UserSearchService {
private OrgService orgService; private OrgService orgService;
@Autowired @Autowired
private RoleService roleService; private RoleService roleService;
@Autowired
private OrgSearchServiceImpl orgSearchServiceImpl;
@Override @Override
public Page<UserSearchVo> queryUser(QueryParameter queryParameter) throws Exception { public Page<UserSearchVo> queryUser(QueryParameter queryParameter) throws Exception {
if (existCondition(queryParameter)) { QueryParameter searchParameter = criteriaHandler(queryParameter);
return existConditionQuery(queryParameter); Page<UserEntity> page = userService.query(searchParameter);
} else {
return noConditionQuery(queryParameter);
}
}
private Page<UserSearchVo> existConditionQuery(QueryParameter queryParameter) throws Exception {
List<Criteria> userCriteriaList = new ArrayList<>();
/**
* 前端已将queryCriteria机构查询条件角色查询条件重新使用 and 操作进行组装后端需提取出来进行处理
* 只要存在查询条件最外层一定是 { fieldName: null, operator: 'and', criteria: [...] }
* 所以最外层无需处理直接处理最外层的 criteria 数组
*/
List<Criteria> criterias = queryParameter.getCriterias().get(0).getCriteria();
for (Criteria criteria: criterias) {
if (criteria.getFieldName()!=null) {
Boolean isOrgCriteria = Arrays.stream(UserSearchConstant.ORG_CRITERIA_FIELD_NAMES).anyMatch(criteria.getFieldName()::equals);
Boolean isRoleCriteria = UserSearchConstant.FN_ROLECRITERIA.equals(criteria.getFieldName());
if (!isOrgCriteria && !isRoleCriteria) {
userCriteriaList.add(criteria);
} else if (isOrgCriteria) {
// 跟机构相关的查询条件处理
orgHandler(criteria.getFieldName(), criteria, userCriteriaList);
} else if (isRoleCriteria) {
// 角色的查询条件处理
roleCriteriaHandler((Equals) criteria, userCriteriaList);
}
} else {
// 非特定 fieldName 添加进用户查询的 criteria 中
userCriteriaList.add(criteria);
}
}
Page<UserEntity> page = userService.query(rebuildQueryParameter(queryParameter, userCriteriaList));
return userEntityPage2UserSearchVoPage(page); return userEntityPage2UserSearchVoPage(page);
} }
private Page<UserSearchVo> noConditionQuery(QueryParameter queryParameter) throws Exception { private QueryParameter criteriaHandler(QueryParameter queryParameter) throws Exception {
Page<UserEntity> page = userService.query(queryParameter); if (queryParameter.existsCriteria()) {
return userEntityPage2UserSearchVoPage(page); List<Criteria> userCriteriaList = new ArrayList<>();
} /**
* 前端已将queryCriteria机构查询条件角色查询条件重新使用 and 操作进行组装后端需提取出来进行处理
private Boolean existCondition(QueryParameter queryParameter) { * 只要存在查询条件最外层一定是 { fieldName: null, operator: 'and', criteria: [...] }
List<Criteria> list = queryParameter.getCriterias(); * 所以最外层无需处理直接处理最外层的 criteria 数组
if (list !=null && list.size() > 0 && list.get(0).getCriteria() != null && list.get(0).getCriteria().size() > 0) { */
return true; List<Criteria> criterias = queryParameter.getCriterias().get(0).getCriteria();
} for (Criteria criteria: criterias) {
return false; if (criteria.getFieldName()!=null) {
} Boolean isOrgCriteria = Arrays.stream(SearchConstant.ORG_CRITERIA_FIELD_NAMES).anyMatch(criteria.getFieldName()::equals);
Boolean isRoleCriteria = SearchConstant.FN_ROLECRITERIA.equals(criteria.getFieldName());
private void orgHandler(String fieldName, Criteria criteria, List<Criteria> userCriteriaList) throws Exception { if (!isOrgCriteria && !isRoleCriteria) {
switch(fieldName) { userCriteriaList.add(criteria);
case UserSearchConstant.FN_ORGCRITERIA: } else if (isOrgCriteria) {
orgCriteriaHandler((Equals) criteria, userCriteriaList); // 跟机构相关的查询条件处理
break; List<String> orgIds = orgSearchServiceImpl.findOrgIds(criteria.getFieldName(), criteria);
case UserSearchConstant.FN_FINDBYORGIDWITHSUB: if (orgIds != null) {
findByOrgIdWithSubHandler((InSet) criteria, userCriteriaList); // 不为 null 添加机构过滤逻辑
break; CriteriaHandler.criteriaListAddInSet(userCriteriaList, "orgs", orgIds);
case UserSearchConstant.FN_FINDBYORGID: }
findByOrgIdHandler((InSet) criteria, userCriteriaList); } else if (isRoleCriteria) {
break; // 角色的查询条件处理
case UserSearchConstant.FN_FINDBYDEFAULTORGWITHSUB: roleCriteriaHandler((Equals) criteria, userCriteriaList);
findByDefaultOrg(userCriteriaList, true);
break;
case UserSearchConstant.FN_FINDBYDEFAULTORG:
findByDefaultOrg(userCriteriaList, false);
break;
}
}
private void orgCriteriaHandler(Equals criteria, List<Criteria> userCriteriaList) throws Exception {
QueryParameter queryParameter = new QueryParameter();
queryParameter.setCriteria(criteria.getValue());
queryParameter.setPageable(false);
List<OrgEntity> orgList = orgService.list(queryParameter);
List<String> orgIds = orgList.stream().map(OrgEntity::getId).collect(Collectors.toList());
criteriaListAddInSet(userCriteriaList, "orgs", orgIds);
}
private void findByOrgIdWithSubHandler(InSet criteria, List<Criteria> userCriteriaList) {
String[] ids = criteria.getValue();
List<OrgEntity> list = orgService.getRepository().findAllById(Arrays.asList(ids));
List<String> orgIds = new ArrayList<>();
if (list!=null && list.size()>0) {
for(OrgEntity org: list) {
orgIds.add(org.getId());
recursiveGetChildren(org, orgIds);
}
}
criteriaListAddInSet(userCriteriaList, "orgs", orgIds);
}
private void findByOrgIdHandler(InSet criteria, List<Criteria> userCriteriaList) {
String[] ids = criteria.getValue();
List<OrgEntity> list = orgService.getRepository().findAllById(Arrays.asList(ids));
List<String> orgIds = list.stream().map(OrgEntity::getId).collect(Collectors.toList());
criteriaListAddInSet(userCriteriaList, "orgs", orgIds);
}
private void findByDefaultOrg(List<Criteria> userCriteriaList, Boolean withSub) {
String orgId = SecurityUtil.getDefaultOrgId();
String fieldName = "orgs";
if (!StringUtil.isNullOrEmpty(orgId)) {
OrgEntity org = orgService.findById(orgId);
if (org!=null) {
// TODO 机构是否为总行机构逻辑还需完善,目前判定 parent 为空为总行机构,多法人情况下该逻辑可能不适用。
if (org.getParent() != null) {
// 非总行机构添加查询条件,总行机构不用添加,查所有用户
List<String> orgIds = new ArrayList<>();
orgIds.add(org.getId());
if (withSub) {
recursiveGetChildren(org, orgIds);
} }
criteriaListAddInSet(userCriteriaList, fieldName, orgIds); } else {
// 非特定 fieldName 添加进用户查询的 criteria 中
userCriteriaList.add(criteria);
} }
} else {
// 默认机构为空不允许查询用户
criteriaListAddInSet(userCriteriaList, fieldName, null);
} }
return CriteriaHandler.buildQueryParameter(queryParameter, userCriteriaList);
} else { } else {
criteriaListAddInSet(userCriteriaList, fieldName, null); return queryParameter;
} }
} }
public void recursiveGetChildren(OrgEntity org, List<String> ids) {
List<OrgEntity> children =org.getChildren();
if(children!=null && !children.isEmpty()){
for(OrgEntity child : children){
ids.add(child.getId());
recursiveGetChildren(child,ids);
}
}
}
public void criteriaListAddInSet(List<Criteria> criteriaList, String fieldName, List<String> value) {
InSet inSet = new InSet();
inSet.setFieldName(fieldName);
inSet.setOperator(OperatorType.inSet);
if (value!=null && value.size()>0) {
inSet.setValue(value.toArray(new String[0]));
} else {
// 如果传入的value为空,设置一个无法匹配到数据的数组
inSet.setValue(new String[]{Long.toString(System.currentTimeMillis())});
}
criteriaList.add(inSet);
}
private void roleCriteriaHandler(Equals roleCriteria, List<Criteria> userCriteriaList) throws Exception { private void roleCriteriaHandler(Equals roleCriteria, List<Criteria> userCriteriaList) throws Exception {
QueryParameter roleQueryParameter = new QueryParameter(); QueryParameter roleQueryParameter = new QueryParameter();
roleQueryParameter.setCriteria(roleCriteria.getValue()); roleQueryParameter.setCriteria(roleCriteria.getValue());
roleQueryParameter.setPageable(false); roleQueryParameter.setPageable(false);
List<RoleEntity> roleList = roleService.list(roleQueryParameter); List<RoleEntity> roleList = roleService.list(roleQueryParameter);
List<String> roleIds = roleList.stream().map(RoleEntity::getId).collect(Collectors.toList()); List<String> roleIds = new ArrayList<>();
criteriaListAddInSet(userCriteriaList, "roles", roleIds); if (roleList!=null && roleList.size() >0) {
} roleIds = roleList.stream().map(RoleEntity::getId).collect(Collectors.toList());
}
public QueryParameter rebuildQueryParameter(QueryParameter oldQueryParameter, List<Criteria> userCriteriaList) { CriteriaHandler.criteriaListAddInSet(userCriteriaList, "roles", roleIds);
QueryParameter queryParameter = new QueryParameter();
queryParameter.setCriterias(userCriteriaList);
queryParameter.setSortBy(oldQueryParameter.getSortBy());
queryParameter.setPageable(oldQueryParameter.getPageable());
queryParameter.setPage(oldQueryParameter.getPage());
queryParameter.setOperator(oldQueryParameter.getOperator());
queryParameter.setSize(oldQueryParameter.getSize());
queryParameter.setExportFilename(oldQueryParameter.getExportFilename());
return queryParameter;
} }
private Page<UserSearchVo> userEntityPage2UserSearchVoPage(Page<UserEntity> page) throws Exception { private Page<UserSearchVo> userEntityPage2UserSearchVoPage(Page<UserEntity> page) throws Exception {

48
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/support/CriteriaHandler.java

@ -0,0 +1,48 @@
package io.sc.platform.lcdp.frontend.component.support;
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 java.util.List;
public class CriteriaHandler {
/**
* 根据 criteria条件集合与原始 QueryParameter 中的分页页码等参数构建一个新的 QueryParameter
* @param queryParameter 原始QueryParameter
* @param criterias criteria条件集合
* @return
*/
public static QueryParameter buildQueryParameter(QueryParameter queryParameter, List<Criteria> criterias) {
QueryParameter newQueryParameter = new QueryParameter();
newQueryParameter.setCriterias(criterias);
newQueryParameter.setSortBy(queryParameter.getSortBy());
newQueryParameter.setPageable(queryParameter.getPageable());
newQueryParameter.setPage(queryParameter.getPage());
newQueryParameter.setOperator(queryParameter.getOperator());
newQueryParameter.setSize(queryParameter.getSize());
newQueryParameter.setExportFilename(queryParameter.getExportFilename());
return newQueryParameter;
}
/**
* 给criteria集合增加 inset 操作
* @param criteriaList 要增加的集合
* @param fieldName inset操作字段名
* @param value inset操作字段值
*/
public static void criteriaListAddInSet(List<Criteria> criteriaList, String fieldName, List<String> value) {
InSet inSet = new InSet();
inSet.setFieldName(fieldName);
inSet.setOperator(OperatorType.inSet);
if (value!=null && value.size()>0) {
inSet.setValue(value.toArray(new String[0]));
} else {
// 如果传入的value为空,设置一个无法匹配到数据的数组
inSet.setValue(new String[]{Long.toString(System.currentTimeMillis())});
}
criteriaList.add(inSet);
}
}

18
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/support/UserSearchConstant.java → io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/support/SearchConstant.java

@ -1,6 +1,6 @@
package io.sc.platform.lcdp.frontend.component.support; package io.sc.platform.lcdp.frontend.component.support;
public class UserSearchConstant { public class SearchConstant {
/** /**
* 角色criteria查询条件 * 角色criteria查询条件
@ -12,23 +12,23 @@ public class UserSearchConstant {
*/ */
public final static String FN_ORGCRITERIA = "orgCriteria"; public final static String FN_ORGCRITERIA = "orgCriteria";
/** /**
* 可选用户根据机构ID数组并包含数组中的所有子机构进行过滤 * 根据机构ID数组并包含数组中的所有子机构进行过滤
*/ */
public final static String FN_FINDBYORGIDWITHSUB = "findByOrgIdWithSub"; public final static String FN_FINDBYORGIDWITHSUB = "findByOrgIdWithSub";
/** /**
* 可选用户根据机构ID数组进行过滤 * 根据机构ID数组进行过滤
*/ */
public final static String FN_FINDBYORGID = "findByOrgId"; public final static String FN_FINDBYORGID = "findByOrgId";
/** /**
* 可选用户根据当前登录用户的默认机构及机构下的所有子机构进行过滤 * 根据当前登录用户的默认机构及机构下的所有子机构进行过滤
* 若默认机构为空无法选择用户 * 若默认机构为空返回空集合
* 为总行的用户可选全部用户 * 为总行的用户可选全部数据
*/ */
public final static String FN_FINDBYDEFAULTORGWITHSUB = "findByDefaultOrgWithSub"; public final static String FN_FINDBYDEFAULTORGWITHSUB = "findByDefaultOrgWithSub";
/** /**
* 可选用户根据当前登录用户的默认机构机构进行过滤 * 根据当前登录用户的默认机构机构进行过滤
* 若默认机构为空无法选择用户 * 若默认机构为空返回空集合
* 为总行的用户可选全部用户 * 为总行的用户可选全部数据
*/ */
public final static String FN_FINDBYDEFAULTORG = "findByDefaultOrg"; public final static String FN_FINDBYDEFAULTORG = "findByDefaultOrg";
/** /**

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

@ -1,46 +0,0 @@
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;
}
}
Loading…
Cancel
Save