Browse Source

1、表单元素大更新

2、表格组件API调整及优化
main
likunming 6 months ago
parent
commit
f6d6b41546
  1. 2
      io.sc.platform.core.frontend/src/components/index.ts
  2. 232
      io.sc.platform.core.frontend/src/mock/api/sample/params.json
  3. 104
      io.sc.platform.core.frontend/src/platform/components/checkbox/WCheckbox.vue
  4. 114
      io.sc.platform.core.frontend/src/platform/components/checkbox/WCheckboxGroup.vue
  5. 321
      io.sc.platform.core.frontend/src/platform/components/code-mirror/WCodeMirror.vue
  6. 76
      io.sc.platform.core.frontend/src/platform/components/code-mirror/w-code-mirror/PlaceholderPlugin.ts
  7. 128
      io.sc.platform.core.frontend/src/platform/components/color/WColorInput.vue
  8. 148
      io.sc.platform.core.frontend/src/platform/components/color/WColorInputPalette.vue
  9. 214
      io.sc.platform.core.frontend/src/platform/components/color/quasarColors.ts
  10. 138
      io.sc.platform.core.frontend/src/platform/components/cron/WCron.vue
  11. 251
      io.sc.platform.core.frontend/src/platform/components/cron/w-cron-segment/DaySegment.vue
  12. 185
      io.sc.platform.core.frontend/src/platform/components/cron/w-cron-segment/HourSegment.vue
  13. 185
      io.sc.platform.core.frontend/src/platform/components/cron/w-cron-segment/MinuteSegment.vue
  14. 197
      io.sc.platform.core.frontend/src/platform/components/cron/w-cron-segment/MonthSegment.vue
  15. 185
      io.sc.platform.core.frontend/src/platform/components/cron/w-cron-segment/SecondSegment.vue
  16. 233
      io.sc.platform.core.frontend/src/platform/components/cron/w-cron-segment/WeekSegment.vue
  17. 93
      io.sc.platform.core.frontend/src/platform/components/cron/w-cron-segment/YearSegment.vue
  18. 78
      io.sc.platform.core.frontend/src/platform/components/date/WDate.vue
  19. 88
      io.sc.platform.core.frontend/src/platform/components/date/WDateRange.vue
  20. 70
      io.sc.platform.core.frontend/src/platform/components/file/WFile.vue
  21. 92
      io.sc.platform.core.frontend/src/platform/components/form/FormField.ts
  22. 60
      io.sc.platform.core.frontend/src/platform/components/form/WForm.vue
  23. 320
      io.sc.platform.core.frontend/src/platform/components/form/WForm_bak.vue
  24. 70
      io.sc.platform.core.frontend/src/platform/components/form/elements/WCheckbox.vue
  25. 205
      io.sc.platform.core.frontend/src/platform/components/form/elements/WColorInput.vue
  26. 440
      io.sc.platform.core.frontend/src/platform/components/form/elements/WColorInputPalette.vue
  27. 161
      io.sc.platform.core.frontend/src/platform/components/form/elements/WDate.vue
  28. 145
      io.sc.platform.core.frontend/src/platform/components/form/elements/WFile.vue
  29. 81
      io.sc.platform.core.frontend/src/platform/components/form/elements/WListGrid.vue
  30. 144
      io.sc.platform.core.frontend/src/platform/components/form/elements/WNumber.vue
  31. 93
      io.sc.platform.core.frontend/src/platform/components/form/elements/WOptionGroup.vue
  32. 143
      io.sc.platform.core.frontend/src/platform/components/form/elements/WPassword.vue
  33. 206
      io.sc.platform.core.frontend/src/platform/components/form/elements/WPosition.vue
  34. 7
      io.sc.platform.core.frontend/src/platform/components/form/elements/WQueryCondition.vue
  35. 144
      io.sc.platform.core.frontend/src/platform/components/form/elements/WSelect.vue
  36. 99
      io.sc.platform.core.frontend/src/platform/components/form/elements/WSelectUserText.vue
  37. 142
      io.sc.platform.core.frontend/src/platform/components/form/elements/WText.vue
  38. 155
      io.sc.platform.core.frontend/src/platform/components/form/elements/WTextBtn.vue
  39. 142
      io.sc.platform.core.frontend/src/platform/components/form/elements/WTextarea.vue
  40. 154
      io.sc.platform.core.frontend/src/platform/components/form/elements/WTreeSelect.vue
  41. 38
      io.sc.platform.core.frontend/src/platform/components/grid/GridBody.vue
  42. 2
      io.sc.platform.core.frontend/src/platform/components/grid/GridConfig.vue
  43. 55
      io.sc.platform.core.frontend/src/platform/components/grid/GridEditToolbar.vue
  44. 12
      io.sc.platform.core.frontend/src/platform/components/grid/GridEditor.vue
  45. 2
      io.sc.platform.core.frontend/src/platform/components/grid/GridHeader.vue
  46. 62
      io.sc.platform.core.frontend/src/platform/components/grid/GridTop.vue
  47. 11
      io.sc.platform.core.frontend/src/platform/components/grid/TreeGridRow.vue
  48. 1163
      io.sc.platform.core.frontend/src/platform/components/grid/WGrid.vue
  49. 6
      io.sc.platform.core.frontend/src/platform/components/grid/ts/grid.ts
  50. 225
      io.sc.platform.core.frontend/src/platform/components/icon/WIcon.vue
  51. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/bootstrap.json
  52. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/eva-icons.json
  53. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/fontawesome-v6-icons-brands.json
  54. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/fontawesome-v6-icons-regular.json
  55. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/fontawesome-v6-icons-solid.json
  56. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/material-icons-outlined.json
  57. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/material-icons-rounded.json
  58. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/material-icons-sharp.json
  59. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/material-icons.json
  60. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/material-symbols-outlined.json
  61. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/material-symbols-rounded.json
  62. 0
      io.sc.platform.core.frontend/src/platform/components/icon/icons/material-symbols-sharp.json
  63. 59
      io.sc.platform.core.frontend/src/platform/components/index.ts
  64. 7
      io.sc.platform.core.frontend/src/platform/components/label/WLabel.vue
  65. 75
      io.sc.platform.core.frontend/src/platform/components/number/WNumber.vue
  66. 66
      io.sc.platform.core.frontend/src/platform/components/password/WPassword.vue
  67. 141
      io.sc.platform.core.frontend/src/platform/components/position/WPosition.vue
  68. 119
      io.sc.platform.core.frontend/src/platform/components/radio/WRadio.vue
  69. 272
      io.sc.platform.core.frontend/src/platform/components/select/WGridSelect.vue
  70. 153
      io.sc.platform.core.frontend/src/platform/components/select/WInputSelect.vue
  71. 259
      io.sc.platform.core.frontend/src/platform/components/select/WOrgSelect.vue
  72. 66
      io.sc.platform.core.frontend/src/platform/components/select/WSelect.vue
  73. 304
      io.sc.platform.core.frontend/src/platform/components/select/WUserSelect.vue
  74. 85
      io.sc.platform.core.frontend/src/platform/components/text-editor/WTextEditor.vue
  75. 80
      io.sc.platform.core.frontend/src/platform/components/text/WText.vue
  76. 65
      io.sc.platform.core.frontend/src/platform/components/textarea/WTextarea.vue
  77. 7
      io.sc.platform.core.frontend/src/platform/index.ts
  78. 14
      io.sc.platform.core.frontend/src/routes/routes.json
  79. 88
      io.sc.platform.core.frontend/src/views/Editor.vue
  80. 363
      io.sc.platform.core.frontend/src/views/likm/Form.vue
  81. 567
      io.sc.platform.core.frontend/src/views/likm/Grid.vue
  82. 65
      io.sc.platform.core.frontend/src/views/likm/TreeGrid.vue
  83. 232
      io.sc.platform.core.frontend/template-project/src/mock/api/sample/params.json
  84. 5
      io.sc.platform.jdbc.driver.oceanbase/build.gradle
  85. 0
      io.sc.platform.jdbc.driver.oceanbase/gradle.properties
  86. 10
      io.sc.platform.jdbc.driver.oceanbase/src/main/resources/META-INF/platform/plugins/jdbc-connection-template.json
  87. 2
      io.sc.platform.mvc.frontend/webpack.env.serve.cjs

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

@ -5,7 +5,6 @@
import component_testcase_openNoMenuRoute from '@/views/testcase/route/OpenNoMenuRoute.vue';
import component_testcase_noMenuRoute from '@/views/testcase/route/NoMenuRoute.vue';
import component_testcase_mathEditor from '@/views/testcase/math/MathEditor.vue';
import component_testcase_mathEditorForm from '@/views/testcase/math/MathEditorForm.vue';
import component_testcase_form from '@/views/testcase/form/form.vue';
import component_testcase_codemirror from '@/views/testcase/code-mirror/code-mirror.vue';
import component_testcase_loading from '@/views/testcase/loading/loading.vue';
@ -26,7 +25,6 @@ const localComponents = {
'component.testcase.openNoMenuRoute': component_testcase_openNoMenuRoute,
'component.testcase.noMenuRoute': component_testcase_noMenuRoute,
'component.testcase.mathEditor': component_testcase_mathEditor,
'component.testcase.mathEditorForm': component_testcase_mathEditorForm,
'component.testcase.form': component_testcase_form,
'component.testcase.codemirror': component_testcase_codemirror,
'component.testcase.loading': component_testcase_loading,

232
io.sc.platform.core.frontend/src/mock/api/sample/params.json

@ -0,0 +1,232 @@
{
"enable": true,
"url": "/api/rwa/params/isc/fetchParams?pageable=false&sortBy=sortNo",
"method": "get",
"response": {
"code" : 200,
"messageI18nKey" : "success",
"message" : "success",
"data" : [ {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2023-09-05 02:02:14",
"lastModifier" : "admin",
"lastModifyDate" : "2023-09-05 02:02:14",
"corporationCode" : null,
"id" : "2268c3e2-c03f-4d8a-8bcd-e37d24cf955e",
"name" : "银行参数",
"parent" : null,
"children" : [ ],
"icon" : "folder",
"isFolder" : true,
"sortNo" : null,
"isDefault" : false,
"pageType" : null,
"templateId" : null,
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2023-08-30 08:00:00",
"lastModifier" : "admin",
"lastModifyDate" : "2023-08-30 08:00:00",
"corporationCode" : null,
"id" : "BIII",
"name" : "BIII参数",
"parent" : null,
"children" : [ {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-26 17:34:22",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-26 17:34:22",
"corporationCode" : "_PRIMARY_",
"id" : "478b6c43-ca5b-45c4-a0cb-14715f45c98c",
"name" : "信用风险-表内风险权重参数表",
"parent" : "BIII",
"ticked" : true,
"children" : [ ],
"icon" : "calendar_month",
"isFolder" : false,
"sortNo" : 100,
"isDefault" : true,
"pageType" : "TEMPLATE",
"templateId" : "47442edb-7b0d-4dc4-ab71-755ddbae71bf",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : null,
"createDate" : null,
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-27 15:41:34",
"corporationCode" : "_PRIMARY_",
"id" : "BWFXQZ",
"name" : "信用风险-表外主体权重参数表",
"parent" : "BIII",
"children" : [ ],
"icon" : "calendar_today",
"isFolder" : false,
"sortNo" : 200,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "1b42b129-b683-4ac0-a8dc-3d39db1eb836",
"routeName" : null,
"routeQuery" : null
} ],
"icon" : "folder",
"isFolder" : true,
"sortNo" : 100,
"isDefault" : false,
"pageType" : null,
"templateId" : null,
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-28 13:56:36",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 13:56:36",
"corporationCode" : "_PRIMARY_",
"id" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"name" : "交易对手信用风险",
"parent" : null,
"children" : [ {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-28 14:03:45",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 14:03:45",
"corporationCode" : "_PRIMARY_",
"id" : "fac9819a-84a7-46bd-9e89-47e07ebeb31b",
"name" : "交易对手信用风险设置",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "settings_suggest",
"isFolder" : false,
"sortNo" : 100,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "61da07af-351f-4c70-9e58-0ab99ca1d730",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-28 14:55:57",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 14:55:57",
"corporationCode" : "_PRIMARY_",
"id" : "2389c168-12c5-4955-b31f-5090f9dcea4b",
"name" : "SACCR监管参数配置",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "settings_applications",
"isFolder" : false,
"sortNo" : 200,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "df6de052-7f56-42bd-895b-ca09088e0906",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : null,
"createDate" : null,
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 16:13:48",
"corporationCode" : "_PRIMARY_",
"id" : "ec48aefe-1181-4764-9a97-b149f20b520e",
"name" : "CVA行业类别映射",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "linear_scale",
"isFolder" : false,
"sortNo" : 300,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "125dc90f-15cb-4b82-961b-1b7361f0df5e",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : null,
"createDate" : null,
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 16:31:40",
"corporationCode" : "_PRIMARY_",
"id" : "a315ede8-3438-4d00-a60a-c9ef2babc0b1",
"name" : "货币优先顺序",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "currency_yen",
"isFolder" : false,
"sortNo" : 400,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "3fcee541-43f6-480e-827b-6cf021f748eb",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-28 16:45:56",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 16:45:56",
"corporationCode" : "_PRIMARY_",
"id" : "8865a5dd-0a88-4362-9ba9-192009072ad0",
"name" : "CVA评级等级映射",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "format_list_numbered",
"isFolder" : false,
"sortNo" : 500,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "637eea6b-1fcf-4977-a56b-9653b0703c31",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-28 17:08:39",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 17:08:39",
"corporationCode" : "_PRIMARY_",
"id" : "62021407-ac21-4fde-a37d-e53865b94aa5",
"name" : "CVA权重映射",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "percent",
"isFolder" : false,
"sortNo" : 600,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "dacd1de2-8492-4123-b1fb-f8abb4f6346b",
"routeName" : null,
"routeQuery" : null
} ],
"icon" : "folder",
"isFolder" : true,
"sortNo" : 300,
"isDefault" : false,
"pageType" : null,
"templateId" : null,
"routeName" : null,
"routeQuery" : null
} ]
}
}

104
io.sc.platform.core.frontend/src/platform/components/checkbox/WCheckbox.vue

@ -0,0 +1,104 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)" :class="props.outlined ? 'w-checkbox' : ''">
<template v-if="props.outlined">
<q-field
ref="checkboxRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
v-bind="attrs"
:outlined="props.outlined"
:borderless="!props.outlined"
:stack-label="true"
:dense="true"
:rules="FieldMethods.getRules(props, modelValue, checkboxRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
<template #append>
<q-checkbox
v-model="modelValue"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
></q-checkbox>
</template>
</q-field>
</template>
<template v-else>
<q-checkbox
ref="checkboxRef"
v-model="modelValue"
v-bind="attrs"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
></q-checkbox>
</template>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const checkboxRef = ref();
const attrs = useAttrs();
const modelValue = defineModel({ type: Boolean, default: false });
interface FieldProps extends FormFieldProps {
outlined?: boolean;
borderless?: boolean;
}
const props = withDefaults(defineProps<FieldProps>(), {
outlined: true,
showIf: true,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
if (props.outlined && checkboxRef.value) {
return checkboxRef.value.validate();
}
return true;
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = false;
};
}
const fieldMethodsClass = new FieldMethods();
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>
<style lang="css">
.w-checkbox .q-field--dense .q-field__label {
font-size: 18px;
top: 18px;
}
.w-checkbox .q-field__marginal {
font-size: 16px;
color: #0f0f0f;
}
</style>

114
io.sc.platform.core.frontend/src/platform/components/checkbox/WCheckboxGroup.vue

@ -0,0 +1,114 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)" :class="props.simple ? 'w-checkbox-group' : ''">
<q-field
ref="checkboxGroupRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
v-bind="attrs"
:outlined="props.outlined"
:borderless="!props.outlined"
:stack-label="true"
:dense="true"
:rules="FieldMethods.getRules(props, modelValue, checkboxGroupRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
<template v-if="!props.simple" #control>
<div class="pt-[5px]">
<q-option-group
v-model="modelValue"
type="checkbox"
:dense="props.dense"
:options="props.options"
:inline="props.inline"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
/>
</div>
</template>
<template v-else #append>
<q-option-group
v-model="modelValue"
type="checkbox"
:dense="props.dense"
:options="props.options"
:inline="props.inline"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
/>
</template>
</q-field>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const checkboxGroupRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<Array<string> | Array<number>>();
interface FieldProps extends FormFieldProps {
dense?: boolean;
inline?: boolean;
outlined?: boolean;
borderless?: boolean;
simple?: boolean; // checkboxlabel使
options: Array<() => void>;
}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
dense: true,
inline: true,
outlined: true,
simple: true,
options: () => {
return [];
},
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return checkboxGroupRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>
<style lang="css">
.w-checkbox-group .q-field--dense .q-field__label {
font-size: 18px;
top: 18px;
}
.w-checkbox-group .q-field__marginal {
font-size: 16px;
color: #0f0f0f;
}
</style>

321
io.sc.platform.core.frontend/src/platform/components/code-mirror/WCodeMirror.vue

@ -0,0 +1,321 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-field
ref="fieldRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
style="position: relative"
:rules="FieldMethods.getRules(props, modelValue, fieldRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
@focus.stop.prevent="focus"
@blur.stop.prevent="blur"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-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(modelValue) && 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, watch } from 'vue';
import { Tools } from '@/platform';
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';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const fieldRef = ref();
const codemirrorRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string>();
interface FieldProps extends FormFieldProps {
//
lang?: string;
//
width?: number | string;
//
height?: number | string;
//
lineHeight?: number | string;
//
rows?: number;
//
toolbar?: boolean;
//
editable?: boolean;
//
lineWrap?: boolean;
// tab size
tabSize?: number;
//
lineNumber?: boolean;
//
lineBreak?: boolean;
//
placeholder?: boolean;
autoCompletion?: () => void;
userDefinedFunctions?: Array<() => void>;
activateOnCompletion?: () => void;
}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
lang: 'json',
width: '100%',
height: undefined,
lineHeight: 20,
rows: 4,
toolbar: true,
editable: true,
lineWrap: false,
tabSize: 4,
lineNumber: false,
lineBreak: true,
placeholder: false,
autoCompletion: undefined,
userDefinedFunctions: () => {
return [];
},
activateOnCompletion: undefined,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return fieldRef.value.validate();
};
setValue = (value_) => {
editorView.dispatch({ changes: { from: 0, to: editorView.state.doc.length, insert: value_ } });
};
getValue = () => {
return editorView.state.doc.toString();
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
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();
}
};
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(FieldMethods.getReadOnly(props, modelValue.value)),
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: modelValue.value,
});
watch(
() => modelValue.value,
() => {
// ,,
if (!isFocus) {
let content = modelValue.value;
if (!props.lineBreak && !Tools.isEmpty(content)) {
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;
};
const blur = () => {
isFocus = false;
let content = editorView.state.doc.toString();
if (Tools.isUndefinedOrNull(content)) {
modelValue.value = '';
}
const reg = /\$\{(.+?)\}\.\$\{(.+?)\}/g;
while (reg.test(content)) {
content = content.replace(reg, '${$1.$2}');
}
modelValue.value = content;
};
const configure = (values) => {
editorView.dispatch({ effects: StateEffect.reconfigure.of(values) });
};
const buttonClick = (button) => {
if (button.click) {
button.click({
value: modelValue.value,
form: props['form'],
});
}
};
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
configure,
});
</script>

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

@ -0,0 +1,76 @@
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;

128
io.sc.platform.core.frontend/src/platform/components/color/WColorInput.vue

@ -0,0 +1,128 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="colorRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="FieldMethods.getRules(props, modelValue, colorRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
<template v-if="!props['form'] || (props['form'] && props['form'].getStatus() !== 'view')" #append>
<!-- 选择颜色按钮 -->
<q-btn
icon="square"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('select')"
:style="{ border: '1px solid #e5e7eb', color: modelValue, 'background-color': computedStoreBackgroundColorValue }"
>
<q-popup-proxy v-model:model-value="isShow" anchor="bottom right" self="top right" :offset="[0, 10]">
<q-color
v-model="modelValue"
:default-value="modelValue"
format-model="hex"
default-view="palette"
style="max-width: 250px"
@update:model-value="fieldMethodsClass.updateValue"
/>
</q-popup-proxy>
</q-btn>
<!-- 恢复默认值按钮 -->
<q-btn
v-if="restore"
icon="bi-arrow-counterclockwise"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('restore')"
:style="{ 'margin-left': '5px', border: '1px solid #e5e7eb', color: restoreValue, 'background-color': computedStoreBackgroundColorValue }"
@click="restoreDefaultValue"
></q-btn>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, toRaw, computed, useAttrs, onBeforeMount } from 'vue';
import { Tools } from '@/platform';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const colorRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string>();
const isShow = ref(false);
const defaultValue = '#000000';
const restoreValue = toRaw(modelValue.value || defaultValue);
interface FieldProps extends FormFieldProps {
restore?: boolean;
}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
restore: false,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
isShow.value = false;
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return colorRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = restoreValue;
};
}
const fieldMethodsClass = new FieldMethods();
const computedStoreBackgroundColorValue = computed(() => {
const color: string = restoreValue;
if (color.toLowerCase() === 'white' || color.toLowerCase() === '#ffffff' || color.toLowerCase() === '#fff') {
return '#eee';
}
return undefined;
});
const restoreDefaultValue = () => {
modelValue.value = restoreValue;
fieldMethodsClass.updateValue(restoreValue);
};
onBeforeMount(() => {
if (Tools.isEmpty(modelValue.value)) {
modelValue.value = defaultValue;
}
});
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

148
io.sc.platform.core.frontend/src/platform/components/color/WColorInputPalette.vue

@ -0,0 +1,148 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="colorRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="FieldMethods.getRules(props, modelValue, colorRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
<template v-if="!props['form'] || (props['form'] && props['form'].getStatus() !== 'view')" #append>
<!-- 选择颜色按钮 -->
<q-btn
icon="bi-palette"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('select')"
:class="`text-${modelValue}`"
:style="{ border: '1px solid #e5e7eb', 'background-color': computedBackgroundColorValue }"
>
<q-popup-proxy v-model:model-value="isShow" anchor="bottom right" self="top right" :offset="[0, 10]">
<div v-for="(colors, index) in quasarColors" :key="index" class="row">
<div
v-for="color in colors"
:key="`${color}`"
:class="`bg-${color}`"
style="min-width: 20px; max-width: 20px; min-height: 20px; max-height: 20px"
@click="colorClick(`${color}`)"
>
<q-tooltip> {{ color }}</q-tooltip>
</div>
</div>
</q-popup-proxy>
</q-btn>
<!-- 恢复默认值按钮 -->
<q-btn
v-if="restore"
icon="bi-arrow-counterclockwise"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('restore')"
:class="`text-${restoreValue}`"
:style="{ 'margin-left': '5px', border: '1px solid #e5e7eb', 'background-color': computedStoreBackgroundColorValue }"
@click="restoreDefaultValue"
></q-btn>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, toRaw, computed, useAttrs, onBeforeMount } from 'vue';
import { Tools } from '@/platform';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
import { quasarColors } from './quasarColors';
const colorRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string>();
const isShow = ref(false);
const defaultValue = 'black';
const restoreValue = toRaw(modelValue.value || defaultValue);
interface FieldProps extends FormFieldProps {
restore?: boolean;
}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
restore: false,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
modelValue.value = value_;
isShow.value = false;
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return colorRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = restoreValue;
};
}
const fieldMethodsClass = new FieldMethods();
const computedBackgroundColorValue = computed(() => {
if (!Tools.isEmpty(modelValue.value)) {
const color: string = toRaw(modelValue.value);
if (color.toLowerCase() === 'white' || color.toLowerCase() === '#ffffff' || color.toLowerCase() === '#fff') {
return '#eee';
}
}
return undefined;
});
const computedStoreBackgroundColorValue = computed(() => {
const color: string = restoreValue;
if (color.toLowerCase() === 'white' || color.toLowerCase() === '#ffffff' || color.toLowerCase() === '#fff') {
return '#eee';
}
return undefined;
});
const colorClick = (value) => {
fieldMethodsClass.updateValue(value);
};
const restoreDefaultValue = () => {
fieldMethodsClass.updateValue(restoreValue);
};
onBeforeMount(() => {
if (Tools.isEmpty(modelValue.value)) {
modelValue.value = defaultValue;
}
});
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

214
io.sc.platform.core.frontend/src/platform/components/color/quasarColors.ts

@ -0,0 +1,214 @@
export const quasarColors = [
['red', 'red-1', 'red-2', 'red-3', 'red-4', 'red-5', 'red-6', 'red-7', 'red-8', 'red-9', 'red-10', 'red-11', 'red-12', 'red-13', 'red-14'],
['pink', 'pink-1', 'pink-2', 'pink-3', 'pink-4', 'pink-5', 'pink-6', 'pink-7', 'pink-8', 'pink-9', 'pink-10', 'pink-11', 'pink-12', 'pink-13', 'pink-14'],
[
'purple',
'purple-1',
'purple-2',
'purple-3',
'purple-4',
'purple-5',
'purple-6',
'purple-7',
'purple-8',
'purple-9',
'purple-10',
'purple-11',
'purple-12',
'purple-13',
'purple-14',
],
[
'deep-purple',
'deep-purple-1',
'deep-purple-2',
'deep-purple-3',
'deep-purple-4',
'deep-purple-5',
'deep-purple-6',
'deep-purple-7',
'deep-purple-8',
'deep-purple-9',
'deep-purple-10',
'deep-purple-11',
'deep-purple-12',
'deep-purple-13',
'deep-purple-14',
],
[
'indigo',
'indigo-1',
'indigo-2',
'indigo-3',
'indigo-4',
'indigo-5',
'indigo-6',
'indigo-7',
'indigo-8',
'indigo-9',
'indigo-10',
'indigo-11',
'indigo-12',
'indigo-13',
'indigo-14',
],
['blue', 'blue-1', 'blue-2', 'blue-3', 'blue-4', 'blue-5', 'blue-6', 'blue-7', 'blue-8', 'blue-9', 'blue-10', 'blue-11', 'blue-12', 'blue-13', 'blue-14'],
[
'light-blue',
'light-blue-1',
'light-blue-2',
'light-blue-3',
'light-blue-4',
'light-blue-5',
'light-blue-6',
'light-blue-7',
'light-blue-8',
'light-blue-9',
'light-blue-10',
'light-blue-11',
'light-blue-12',
'light-blue-13',
'light-blue-14',
],
['cyan', 'cyan-1', 'cyan-2', 'cyan-3', 'cyan-4', 'cyan-5', 'cyan-6', 'cyan-7', 'cyan-8', 'cyan-9', 'cyan-10', 'cyan-11', 'cyan-12', 'cyan-13', 'cyan-14'],
['teal', 'teal-1', 'teal-2', 'teal-3', 'teal-4', 'teal-5', 'teal-6', 'teal-7', 'teal-8', 'teal-9', 'teal-10', 'teal-11', 'teal-12', 'teal-13', 'teal-14'],
[
'green',
'green-1',
'green-2',
'green-3',
'green-4',
'green-5',
'green-6',
'green-7',
'green-8',
'green-9',
'green-10',
'green-11',
'green-12',
'green-13',
'green-14',
],
[
'light-green',
'light-green-1',
'light-green-2',
'light-green-3',
'light-green-4',
'light-green-5',
'light-green-6',
'light-green-7',
'light-green-8',
'light-green-9',
'light-green-10',
'light-green-11',
'light-green-12',
'light-green-13',
'light-green-14',
],
['lime', 'lime-1', 'lime-2', 'lime-3', 'lime-4', 'lime-5', 'lime-6', 'lime-7', 'lime-8', 'lime-9', 'lime-10', 'lime-11', 'lime-12', 'lime-13', 'lime-14'],
[
'yellow',
'yellow-1',
'yellow-2',
'yellow-3',
'yellow-4',
'yellow-5',
'yellow-6',
'yellow-7',
'yellow-8',
'yellow-9',
'yellow-10',
'yellow-11',
'yellow-12',
'yellow-13',
'yellow-14',
],
[
'amber',
'amber-1',
'amber-2',
'amber-3',
'amber-4',
'amber-5',
'amber-6',
'amber-7',
'amber-8',
'amber-9',
'amber-10',
'amber-11',
'amber-12',
'amber-13',
'amber-14',
],
[
'orange',
'orange-1',
'orange-2',
'orange-3',
'orange-4',
'orange-5',
'orange-6',
'orange-7',
'orange-8',
'orange-9',
'orange-10',
'orange-11',
'orange-12',
'orange-13',
'orange-14',
],
[
'deep-orange',
'deep-orange-1',
'deep-orange-2',
'deep-orange-3',
'deep-orange-4',
'deep-orange-5',
'deep-orange-6',
'deep-orange-7',
'deep-orange-8',
'deep-orange-9',
'deep-orange-10',
'deep-orange-11',
'deep-orange-12',
'deep-orange-13',
'deep-orange-14',
],
[
'brown',
'brown-1',
'brown-2',
'brown-3',
'brown-4',
'brown-5',
'brown-6',
'brown-7',
'brown-8',
'brown-9',
'brown-10',
'brown-11',
'brown-12',
'brown-13',
'brown-14',
],
['grey', 'grey-1', 'grey-2', 'grey-3', 'grey-4', 'grey-5', 'grey-6', 'grey-7', 'grey-8', 'grey-9', 'grey-10', 'grey-11', 'grey-12', 'grey-13', 'grey-14'],
[
'blue-grey',
'blue-grey-1',
'blue-grey-2',
'blue-grey-3',
'blue-grey-4',
'blue-grey-5',
'blue-grey-6',
'blue-grey-7',
'blue-grey-8',
'blue-grey-9',
'blue-grey-10',
'blue-grey-11',
'blue-grey-12',
'blue-grey-13',
'blue-grey-14',
],
['white', 'black'],
];

138
io.sc.platform.core.frontend/src/platform/components/cron/WCron.vue

@ -0,0 +1,138 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="cronRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="FieldMethods.getRules(props, modelValue, cronRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
:v-bind="attrs"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-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 } from 'vue';
import { Tools } from '@/platform';
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';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const cronRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string>();
const isShow = ref(false);
const selectedTab = ref('second');
const splits = (modelValue.value || '').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]);
interface FieldProps extends FormFieldProps {}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return cronRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
const valueChanged = () => {
const values = <any>[];
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, ' ');
modelValue.value = result;
fieldMethodsClass.updateValue(result);
};
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

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

@ -0,0 +1,251 @@
<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/cron/w-cron-segment/HourSegment.vue

@ -0,0 +1,185 @@
<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/cron/w-cron-segment/MinuteSegment.vue

@ -0,0 +1,185 @@
<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/cron/w-cron-segment/MonthSegment.vue

@ -0,0 +1,197 @@
<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/cron/w-cron-segment/SecondSegment.vue

@ -0,0 +1,185 @@
<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/cron/w-cron-segment/WeekSegment.vue

@ -0,0 +1,233 @@
<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/cron/w-cron-segment/YearSegment.vue

@ -0,0 +1,93 @@
<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>

78
io.sc.platform.core.frontend/src/platform/components/date/WDate.vue

@ -0,0 +1,78 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="dateRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="FieldMethods.getRules(props, modelValue, dateRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
@focus="
() => {
dateRef?.blur();
}
"
>
<template #label>
<w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label>
</template>
<template v-if="!FieldMethods.getReadOnly(props, modelValue)" #append>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
<q-date v-model="modelValue" today-btn mask="YYYY-MM-DD" @update:model-value="fieldMethodsClass.updateValue"> </q-date>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const dateRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string>();
interface FieldProps extends FormFieldProps {}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return dateRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

88
io.sc.platform.core.frontend/src/platform/components/date/WDateRange.vue

@ -0,0 +1,88 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="dateRangeRef"
v-model="displayValueComputed"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:clearable="false"
:rules="FieldMethods.getRules(props, modelValue, dateRangeRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
@focus="
() => {
dateRangeRef?.blur();
}
"
>
<template #label>
<w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label>
</template>
<template v-if="!FieldMethods.getReadOnly(props, modelValue)" #append>
<q-btn v-if="!Tools.isEmpty(displayValueComputed)" flat square unelevated dense icon="cancel" @click="fieldMethodsClass.clearValue"></q-btn>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
<q-date v-model="modelValue" today-btn mask="YYYY-MM-DD" :range="true" @update:model-value="fieldMethodsClass.updateValue"> </q-date>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs, computed } from 'vue';
import { Tools } from '@/platform';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const dateRangeRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<object>();
interface FieldProps extends FormFieldProps {}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return dateRangeRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
const displayValueComputed = computed(() => {
if (modelValue.value) {
return modelValue.value['from'] + ' 至 ' + modelValue.value['to'];
}
return '';
});
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

70
io.sc.platform.core.frontend/src/platform/components/file/WFile.vue

@ -0,0 +1,70 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-file
ref="fileRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
:clearable="true"
v-bind="attrs"
:rules="FieldMethods.getRules(props, modelValue, fileRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label>
<w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label>
</template>
<template v-if="!FieldMethods.getReadOnly(props, modelValue)" #append>
<q-icon name="attachment" />
</template>
</q-file>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const fileRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<File | Array<File>>();
interface FieldProps extends FormFieldProps {}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return fileRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

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

@ -0,0 +1,92 @@
import { Tools } from '@/platform';
import { FormValidators } from '@/platform/components';
export interface FormFieldProps {
name?: string;
form?: object; // form对象实例
showIf?: boolean | (() => void); // 是否显示
requiredIf?: boolean | (() => void); // 是否必填
readOnlyIf?: boolean | (() => void); // 是否只读
disableIf?: boolean | (() => void); // 是否禁用
rules?: Array<() => void>; // 验证规则集合
onUpdateValue?: () => void; // 值改变事件函数
}
export abstract class FormFieldMethods {
// 值改变事件
abstract updateValue(value_): void;
// 验证方法
abstract validate(): Promise<boolean> | boolean;
// 设置值
abstract setValue(value_): void;
// 获取值
abstract getValue(): any;
// 清空值
abstract clearValue(): void;
// 获取验证规则集结果
static getRules = (props_, value_, componentRef_, defaultValue_) => {
let result = props_.rules || <any>[];
if (!Tools.isEmpty(defaultValue_)) {
result.push(...defaultValue_);
}
const showResult = FormFieldMethods.getShow(props_, value_);
const requiredResult = FormFieldMethods.getRequired(props_, value_);
if (showResult && requiredResult) {
result.push(FormValidators.required());
} else if (!showResult) {
result = [];
}
if (componentRef_?.value) {
componentRef_.value.resetValidation();
}
return result;
};
// 获取是否必填结果
static getRequired = (props_, value_) => {
if (!Tools.isEmpty(props_.requiredIf)) {
if (typeof props_.requiredIf === 'boolean') {
return props_.requiredIf;
} else if (typeof props_.requiredIf === 'function') {
return props_.requiredIf({ value: value_, form: props_.form }) || false;
}
}
return false;
};
// 获取是否显示结果
static getShow = (props_, value_) => {
if (!Tools.isEmpty(props_.showIf)) {
if (typeof props_.showIf === 'boolean') {
return props_.showIf;
} else if (typeof props_.showIf === 'function') {
return props_.showIf({ value: value_, form: props_.form }) || false;
}
}
return true;
};
// 获取是否只读结果
static getReadOnly = (props_, value_) => {
if (props_.form && props_.form.getStatus() === 'view') {
return true;
}
if (!Tools.isEmpty(props_.readOnlyIf)) {
if (typeof props_.readOnlyIf === 'boolean') {
return props_.readOnlyIf;
} else if (typeof props_.readOnlyIf === 'function') {
return props_.readOnlyIf({ value: value_, form: props_.form }) || false;
}
}
return false;
};
// 获取是否禁用结果
static getDisable = (props_, value_) => {
if (!Tools.isEmpty(props_.disableIf)) {
if (typeof props_.disableIf === 'boolean') {
return props_.disableIf;
} else if (typeof props_.disableIf === 'function') {
return props_.disableIf({ value: value_, form: props_.form }) || false;
}
}
return false;
};
}

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

@ -5,7 +5,7 @@
<template v-for="(field, index) in fieldsComputed as any" :key="String(index)">
<template v-if="field.name">
<component
:is="fiedType[field.type] || field.type"
:is="field.type"
:ref="(el) => setComponentRef(el, field.name)"
v-model="formData[field.name]"
v-bind="field"
@ -23,7 +23,7 @@
</template>
<template v-else>
<component
:is="fiedType[field.type] || field.type"
:is="field.type"
:ref="(el) => setComponentRef(el, field.name)"
:form-ref="formRef"
v-bind="field"
@ -46,9 +46,9 @@
</template>
<script setup lang="ts">
import { ref, reactive, watch, computed, toRaw, useAttrs, getCurrentInstance, nextTick, onUpdated } from 'vue';
import { ref, reactive, watch, computed, toRaw, useAttrs, getCurrentInstance } from 'vue';
import { useQuasar } from 'quasar';
import { VueTools, Tools, eventBus } from '@/platform';
import { VueTools, Tools } from '@/platform';
import { PageStatusEnum } from '@/platform/components/utils';
const $q = useQuasar();
@ -81,46 +81,24 @@ const formFields = {};
let fields_ = ref([...props.fields]);
// colsNum 0
const screenCols = { xs: 1, sm: 2, md: 3, lg: 4, xl: 6 };
const fiedType = {
checkbox: 'w-checkbox',
cron: 'w-cron',
'color-input': 'w-color-input',
colorInput: 'w-color-input',
'color-input-palette': 'w-color-input-palette',
colorInputPalette: 'w-color-input-palette',
'code-mirror': 'w-code-mirror',
codeMirror: 'w-code-mirror',
position: 'w-position',
date: 'w-date',
icon: 'w-icon',
number: 'w-number',
select: 'w-select',
treeSelect: 'w-tree-select',
'tree-select': 'w-tree-select',
text: 'w-text',
textarea: 'w-textarea',
'text-btn': 'w-text-btn',
textBtn: 'w-text-btn',
password: 'w-password',
'option-group': 'w-option-group',
optionGroup: 'w-option-group',
file: 'w-file',
expression: 'w-expression',
math: 'w-math',
};
const defaultValueHandler = (field) => {
if (!Tools.isUndefinedOrNull(field.defaultValue)) {
return field.defaultValue;
} else if (field.type === 'checkbox' || field.type === 'w-checkbox') {
} else if (field.type === 'w-checkbox') {
return false;
} else if (field.type === 'option-group' || field.type === 'optionGroup' || field.type === 'w-option-group') {
} else if (field.type === 'w-checkbox-group') {
return [];
} else if (field.type === 'w-option-group') {
if (!field.optionType || field.optionType === 'radio') {
return undefined;
} else {
return [];
}
} else if ((field.type === 'select' || field.type === 'w-select') && field.multiple) {
} else if (
(field.type === 'w-select' || field.type === 'w-user-select' || field.type === 'w-org-select' || field.type === 'w-grid-select') &&
field.multiple
) {
return [];
}
return undefined;
@ -141,20 +119,6 @@ watch(
},
);
// eventBus.on('onLocaleChanged', (local) => {
// console.info('localeChanged====0----');
// nextTick(() => {
// console.info('localeChanged====1----', props.fields);
// fields_ = ref([...props.fields]);
// for (const field of fields_.value as any) {
// if (field.name) {
// formFields[field.name] = field;
// }
// }
// localFlag.value = !localFlag.value;
// console.info('localeChanged====2----', fields_.value);
// });
// });
watch(
() => props.fields.map((item) => item['label']),
(newVal, oldVal) => {

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

@ -0,0 +1,320 @@
<template>
<div>
<q-form ref="formRef" :autofocus="false" :greedy="true" v-bind="attrs">
<div v-if="fieldsComputed.length > 0" class="grid" :class="formLayoutComputed">
<template v-for="(field, index) in fieldsComputed as any" :key="String(index)">
<template v-if="field.name">
<component
:is="field.type"
:ref="(el) => setComponentRef(el, field.name)"
v-model="formData[field.name]"
v-bind="field"
:form="instance"
:class="
(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])"
></component>
</template>
<template v-else>
<component
:is="field.type"
:ref="(el) => setComponentRef(el, field.name)"
:form-ref="formRef"
v-bind="field"
:form="instance"
:class="
(field.colsFirst ? 'col-start-1 ' : ' ') +
(field.colSpan === 'full'
? ' col-span-' + screenColsNumComputed
: field.colSpan && screenColsNumComputed >= field.colSpan
? ' col-span-' + field.colSpan
: ' col-span-1')
"
></component>
</template>
</template>
</div>
<slot></slot>
</q-form>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, watch, computed, toRaw, useAttrs, getCurrentInstance } from 'vue';
import { useQuasar } from 'quasar';
import { VueTools, Tools } from '@/platform';
import { PageStatusEnum } from '@/platform/components/utils';
const $q = useQuasar();
const attrs = useAttrs();
const componentRef = ref({});
const props = defineProps({
modelValue: {
type: Object,
default: () => {
return undefined;
},
},
colsNum: { type: [Number, Object], default: 0 },
colsXGap: { type: Number, default: 8 },
colsYGap: { type: Number, default: 4 },
fields: {
type: Array,
default: () => {
return [];
},
},
});
const localFlag = ref(false);
const formRef = ref();
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 };
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(
() => props.fields,
(newVal, oldVal) => {
if (newVal.length !== oldVal.length) {
fields_ = ref([...props.fields]);
for (const field of fields_.value as any) {
if (field.name) {
formModel[field.name] = defaultValueHandler(field);
formFields[field.name] = field;
}
}
}
},
);
watch(
() => 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(() => {
localFlag.value;
return fields_.value;
});
for (const field of fields_.value as any) {
if (field.name) {
formModel[field.name] = defaultValueHandler(field);
formFields[field.name] = field;
}
}
let formData = reactive(!Tools.isEmpty(props.modelValue) ? props.modelValue : formModel);
watch(
() => props.modelValue,
(newVal, oldVal) => {
formData = reactive(!Tools.isEmpty(props.modelValue) ? props.modelValue : formModel);
},
);
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 formLayoutComputed = 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;
});
const updateModelValue = (name, value) => {
if (formFields[name].error) {
formFields[name].error = false;
formFields[name].errorMessage = undefined;
}
};
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) => {
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] = defaultValueHandler(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) => {
return obj.errorMessage;
})
.join('、');
}
}
}
};
const getFields = () => {
return formFields;
};
const getFieldComponent = (name) => {
return componentRef.value[name];
};
defineExpose({
getFields,
getData,
setData,
reset,
validate,
getFieldValue,
setFieldValue,
setStatus,
getStatus,
getColsNum,
setValidationErrors,
getFieldComponent,
});
const instance = getCurrentInstance();
VueTools.expose2Instance(instance);
</script>

70
io.sc.platform.core.frontend/src/platform/components/form/elements/WCheckbox.vue

@ -1,70 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-checkbox v-model="checkboxValue" v-bind="attrs" :disable="disableIfComputed" @update:model-value="updateModelValue" @change="changeValue"></q-checkbox>
</div>
</template>
<script setup lang="ts">
import { computed, useAttrs, ref, watch } from 'vue';
const attrs = useAttrs();
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: Boolean, default: false },
showIf: {
type: Function,
default: () => {
return true;
},
},
disableIf: {
type: Function,
default: () => {
return false;
},
},
form: {
type: Object,
default: undefined,
},
});
const emit = defineEmits(['update:modelValue', 'change']);
const checkboxValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
checkboxValue.value = newVal;
},
);
const showIfComputed = computed(() => {
return props.showIf({
value: checkboxValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
if (props.form && props.form.getStatus() === 'view') {
return true;
}
return props.disableIf({
value: checkboxValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', value, props.form);
changeValue(value);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
</script>

205
io.sc.platform.core.frontend/src/platform/components/form/elements/WColorInput.vue

@ -1,205 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-input
ref="colorRef"
v-model="colorValueRef"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@update:model-value="colorChanged"
@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="square"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('select')"
:style="{ border: '1px solid #e5e7eb', color: computedColorValue, 'background-color': computedStoreBackgroundColorValue }"
>
<q-popup-proxy v-model:model-value="isShow" anchor="bottom right" self="top right" :offset="[0, 10]">
<q-color
v-model="colorValueRef"
default-value="colorValueRef"
format-model="hex"
default-view="palette"
style="max-width: 250px"
@update:model-value="colorChanged"
@change="changeValue"
/>
</q-popup-proxy>
</q-btn>
<!-- 恢复默认值按钮 -->
<q-btn
v-if="restore"
icon="bi-arrow-counterclockwise"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('restore')"
:style="{ 'margin-left': '5px', border: '1px solid #e5e7eb', color: restoreValue, 'background-color': computedStoreBackgroundColorValue }"
@click="restoreDefaultValue"
></q-btn>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, toRaw, computed, useAttrs, watch } from 'vue';
import { Tools } from '@/platform';
import { FormValidators } from '@/platform/components';
const attrs = useAttrs();
const rules = attrs.rules;
const colorRef = ref();
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: String, default: '' },
restore: { type: Boolean, default: false },
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 defaultValue = '#000000';
const colorValueRef = ref(props.modelValue || defaultValue);
if (Tools.isEmpty(props.modelValue)) {
emit('update:modelValue', colorValueRef.value);
}
const restoreValue = toRaw(props.modelValue || defaultValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
colorValueRef.value = newVal;
},
);
const computedColorValue = computed(() => {
return colorValueRef.value;
});
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (colorRef?.value) {
colorRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: colorValueRef.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: colorValueRef.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: colorValueRef.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: colorValueRef.value,
form: props.form,
});
});
const computedStoreBackgroundColorValue = computed(() => {
const color: string = restoreValue;
if (color.toLowerCase() === 'white' || color.toLowerCase() === '#ffffff' || color.toLowerCase() === '#fff') {
return '#eee';
}
return undefined;
});
const colorChanged = (value) => {
emit('update:modelValue', value, props.form);
isShow.value = false;
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const restoreDefaultValue = () => {
colorValueRef.value = restoreValue;
emit('update:modelValue', restoreValue, props.form);
};
const validate = () => {
return colorRef.value.validate();
};
defineExpose({
validate,
});
</script>

440
io.sc.platform.core.frontend/src/platform/components/form/elements/WColorInputPalette.vue

@ -1,440 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-input
ref="colorRef"
v-model="colorValueRef"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@update:model-value="colorSelected"
@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-palette"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('select')"
:class="`text-${computedColorValue}`"
:style="{ border: '1px solid #e5e7eb', 'background-color': computedBackgroundColorValue }"
>
<q-popup-proxy v-model:model-value="isShow" anchor="bottom right" self="top right" :offset="[0, 10]">
<div v-for="(colors, index) in quasarColors" :key="index" class="row">
<div
v-for="color in colors"
:key="`${color}`"
:class="`bg-${color}`"
style="min-width: 20px; max-width: 20px; min-height: 20px; max-height: 20px"
@click="colorClick(`${color}`)"
>
<q-tooltip> {{ color }}</q-tooltip>
</div>
</div>
</q-popup-proxy>
</q-btn>
<!-- 恢复默认值按钮 -->
<q-btn
v-if="restore"
icon="bi-arrow-counterclockwise"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('restore')"
:class="`text-${restoreValue}`"
:style="{ 'margin-left': '5px', border: '1px solid #e5e7eb', 'background-color': computedStoreBackgroundColorValue }"
@click="restoreDefaultValue"
></q-btn>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, toRaw, computed, useAttrs, watch } from 'vue';
import { Tools } from '@/platform';
import { FormValidators } from '@/platform/components';
const attrs = useAttrs();
const rules = attrs.rules;
const colorRef = ref();
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: String, default: '' },
restore: { type: Boolean, default: false },
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 quasarColors = [
['red', 'red-1', 'red-2', 'red-3', 'red-4', 'red-5', 'red-6', 'red-7', 'red-8', 'red-9', 'red-10', 'red-11', 'red-12', 'red-13', 'red-14'],
['pink', 'pink-1', 'pink-2', 'pink-3', 'pink-4', 'pink-5', 'pink-6', 'pink-7', 'pink-8', 'pink-9', 'pink-10', 'pink-11', 'pink-12', 'pink-13', 'pink-14'],
[
'purple',
'purple-1',
'purple-2',
'purple-3',
'purple-4',
'purple-5',
'purple-6',
'purple-7',
'purple-8',
'purple-9',
'purple-10',
'purple-11',
'purple-12',
'purple-13',
'purple-14',
],
[
'deep-purple',
'deep-purple-1',
'deep-purple-2',
'deep-purple-3',
'deep-purple-4',
'deep-purple-5',
'deep-purple-6',
'deep-purple-7',
'deep-purple-8',
'deep-purple-9',
'deep-purple-10',
'deep-purple-11',
'deep-purple-12',
'deep-purple-13',
'deep-purple-14',
],
[
'indigo',
'indigo-1',
'indigo-2',
'indigo-3',
'indigo-4',
'indigo-5',
'indigo-6',
'indigo-7',
'indigo-8',
'indigo-9',
'indigo-10',
'indigo-11',
'indigo-12',
'indigo-13',
'indigo-14',
],
['blue', 'blue-1', 'blue-2', 'blue-3', 'blue-4', 'blue-5', 'blue-6', 'blue-7', 'blue-8', 'blue-9', 'blue-10', 'blue-11', 'blue-12', 'blue-13', 'blue-14'],
[
'light-blue',
'light-blue-1',
'light-blue-2',
'light-blue-3',
'light-blue-4',
'light-blue-5',
'light-blue-6',
'light-blue-7',
'light-blue-8',
'light-blue-9',
'light-blue-10',
'light-blue-11',
'light-blue-12',
'light-blue-13',
'light-blue-14',
],
['cyan', 'cyan-1', 'cyan-2', 'cyan-3', 'cyan-4', 'cyan-5', 'cyan-6', 'cyan-7', 'cyan-8', 'cyan-9', 'cyan-10', 'cyan-11', 'cyan-12', 'cyan-13', 'cyan-14'],
['teal', 'teal-1', 'teal-2', 'teal-3', 'teal-4', 'teal-5', 'teal-6', 'teal-7', 'teal-8', 'teal-9', 'teal-10', 'teal-11', 'teal-12', 'teal-13', 'teal-14'],
[
'green',
'green-1',
'green-2',
'green-3',
'green-4',
'green-5',
'green-6',
'green-7',
'green-8',
'green-9',
'green-10',
'green-11',
'green-12',
'green-13',
'green-14',
],
[
'light-green',
'light-green-1',
'light-green-2',
'light-green-3',
'light-green-4',
'light-green-5',
'light-green-6',
'light-green-7',
'light-green-8',
'light-green-9',
'light-green-10',
'light-green-11',
'light-green-12',
'light-green-13',
'light-green-14',
],
['lime', 'lime-1', 'lime-2', 'lime-3', 'lime-4', 'lime-5', 'lime-6', 'lime-7', 'lime-8', 'lime-9', 'lime-10', 'lime-11', 'lime-12', 'lime-13', 'lime-14'],
[
'yellow',
'yellow-1',
'yellow-2',
'yellow-3',
'yellow-4',
'yellow-5',
'yellow-6',
'yellow-7',
'yellow-8',
'yellow-9',
'yellow-10',
'yellow-11',
'yellow-12',
'yellow-13',
'yellow-14',
],
[
'amber',
'amber-1',
'amber-2',
'amber-3',
'amber-4',
'amber-5',
'amber-6',
'amber-7',
'amber-8',
'amber-9',
'amber-10',
'amber-11',
'amber-12',
'amber-13',
'amber-14',
],
[
'orange',
'orange-1',
'orange-2',
'orange-3',
'orange-4',
'orange-5',
'orange-6',
'orange-7',
'orange-8',
'orange-9',
'orange-10',
'orange-11',
'orange-12',
'orange-13',
'orange-14',
],
[
'deep-orange',
'deep-orange-1',
'deep-orange-2',
'deep-orange-3',
'deep-orange-4',
'deep-orange-5',
'deep-orange-6',
'deep-orange-7',
'deep-orange-8',
'deep-orange-9',
'deep-orange-10',
'deep-orange-11',
'deep-orange-12',
'deep-orange-13',
'deep-orange-14',
],
[
'brown',
'brown-1',
'brown-2',
'brown-3',
'brown-4',
'brown-5',
'brown-6',
'brown-7',
'brown-8',
'brown-9',
'brown-10',
'brown-11',
'brown-12',
'brown-13',
'brown-14',
],
['grey', 'grey-1', 'grey-2', 'grey-3', 'grey-4', 'grey-5', 'grey-6', 'grey-7', 'grey-8', 'grey-9', 'grey-10', 'grey-11', 'grey-12', 'grey-13', 'grey-14'],
[
'blue-grey',
'blue-grey-1',
'blue-grey-2',
'blue-grey-3',
'blue-grey-4',
'blue-grey-5',
'blue-grey-6',
'blue-grey-7',
'blue-grey-8',
'blue-grey-9',
'blue-grey-10',
'blue-grey-11',
'blue-grey-12',
'blue-grey-13',
'blue-grey-14',
],
['white', 'black'],
];
const isShow = ref(false);
const defaultValue = 'black';
const colorValueRef = ref(props.modelValue || defaultValue);
if (Tools.isEmpty(props.modelValue)) {
emit('update:modelValue', {
value: colorValueRef.value,
form: props.form,
});
}
const restoreValue = toRaw(props.modelValue || defaultValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
colorValueRef.value = newVal;
},
);
const computedColorValue = computed(() => {
return colorValueRef.value;
});
const computedBackgroundColorValue = computed(() => {
const color: string = toRaw(colorValueRef.value);
if (color.toLowerCase() === 'white' || color.toLowerCase() === '#ffffff' || color.toLowerCase() === '#fff') {
return '#eee';
}
return undefined;
});
const computedStoreBackgroundColorValue = computed(() => {
const color: string = restoreValue;
if (color.toLowerCase() === 'white' || color.toLowerCase() === '#ffffff' || color.toLowerCase() === '#fff') {
return '#eee';
}
return undefined;
});
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (colorRef?.value) {
colorRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: colorValueRef.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: colorValueRef.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: colorValueRef.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: colorValueRef.value,
form: props.form,
});
});
const colorSelected = (value) => {
colorValueRef.value = value;
emit('update:modelValue', value, props.form);
isShow.value = false;
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const colorClick = (value) => {
colorSelected(value);
changeValue(value);
};
const restoreDefaultValue = () => {
colorValueRef.value = restoreValue;
emit('update:modelValue', restoreValue, props.form);
};
const validate = () => {
return colorRef.value.validate();
};
defineExpose({
validate,
});
</script>

161
io.sc.platform.core.frontend/src/platform/components/form/elements/WDate.vue

@ -1,161 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-input
ref="dateRef"
v-model="dateValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@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-icon :name="IconEnum.日期" class="cursor-pointer">
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
<q-date v-model="dateValue" today-btn mask="YYYY-MM-DD" @update:model-value="dateSelected">
<div class="row items-center justify-end">
<q-btn v-close-popup label="关闭" color="primary" flat />
</div>
</q-date>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, watch } from 'vue';
import { FormValidators } from '@/platform/components';
import { IconEnum } from '@/platform/enums';
const dateRef = ref();
const attrs = useAttrs();
const rules = attrs.rules;
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
type: {
type: String,
default: 'w-date',
},
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 dateValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
dateValue.value = newVal;
},
);
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (dateRef?.value) {
dateRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: dateValue.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: dateValue.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: dateValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: dateValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', value, props.form);
};
const dateSelected = (value) => {
updateModelValue(value);
changeValue(value);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const validate = () => {
return dateRef.value.validate();
};
defineExpose({
validate,
});
</script>

145
io.sc.platform.core.frontend/src/platform/components/form/elements/WFile.vue

@ -1,145 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-file
ref="fileRef"
v-model="fileValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
:clearable="true"
v-bind="attrs"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@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-icon name="attachment" />
</template>
</q-file>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, watch } from 'vue';
import { FormValidators } from '@/platform/components';
const fileRef = ref();
const attrs = useAttrs();
const rules = attrs.rules;
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: [File, Array], default: undefined },
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 fileValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
fileValue.value = newVal;
},
);
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (fileRef?.value) {
fileRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: fileValue.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: fileValue.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: fileValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: fileValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', value, props.form);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const validate = () => {
return fileRef.value.validate();
};
defineExpose({
validate,
});
</script>

81
io.sc.platform.core.frontend/src/platform/components/form/elements/WListGrid.vue

@ -1,81 +0,0 @@
<template>
<div v-show="showIfComputed">
<w-grid ref="gridRef" v-bind="attrs"></w-grid>
</div>
</template>
<script setup lang="ts">
import { computed, useAttrs, ref, watch } from 'vue';
const attrs = useAttrs();
const rules = attrs.rules;
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: String, default: undefined },
showIf: {
type: Function,
default: () => {
return true;
},
},
disableIf: {
type: Function,
default: () => {
return false;
},
},
form: {
type: Object,
default: undefined,
},
});
const emit = defineEmits(['update:modelValue', 'change']);
const gridRef =ref();
const gridValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
gridValue.value = newVal;
},
);
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (!showIfComputed.value) {
result = [];
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: gridValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
if (props.form && props.form.getStatus() === 'view') {
return true;
}
return props.disableIf({
value: gridValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', value, props.form);
changeValue(value);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
</script>

144
io.sc.platform.core.frontend/src/platform/components/form/elements/WNumber.vue

@ -1,144 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-input
ref="numberRef"
v-model="numberValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="number"
title=""
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@update:model-value="updateModelValue"
@change="changeValue"
>
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, watch } from 'vue';
import { FormValidators } from '@/platform/components';
const numberRef = ref();
const attrs = useAttrs();
const rules = attrs.rules;
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: Number, default: undefined },
precision: { type: Number, default: 0 },
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 numberValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
numberValue.value = newVal;
},
);
const rulesComputed = computed(() => {
let result = rules || <any>[];
result.push(FormValidators.maxPrecision(props.precision));
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (numberRef?.value) {
numberRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: numberValue.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: numberValue.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: numberValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: numberValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', Number(value), props.form);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const validate = () => {
return numberRef.value.validate();
};
defineExpose({
validate,
});
</script>

93
io.sc.platform.core.frontend/src/platform/components/form/elements/WOptionGroup.vue

@ -1,93 +0,0 @@
<template>
<div v-show="showIfComputed" class="border-solid border">
<div style="color: rgba(0, 0, 0, 0.6)" class="px-1">
{{ label }}
</div>
<q-separator />
<q-option-group
v-model="optionGroupValue"
v-bind="attrs"
:type="optionType"
:disable="disableIfComputed"
@update:model-value="updateModelValue"
@change="changeValue"
/>
</div>
</template>
<script setup lang="ts">
import { computed, useAttrs, ref, watch } from 'vue';
const attrs = useAttrs();
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: [Number, Boolean, String, Array], default: undefined },
type: {
type: String,
default: '',
},
label: {
type: String,
default: '',
},
optionType: {
type: String,
default: 'radio',
},
showIf: {
type: Function,
default: () => {
return true;
},
},
disableIf: {
type: Function,
default: () => {
return false;
},
},
form: {
type: Object,
default: undefined,
},
});
const emit = defineEmits(['update:modelValue', 'change']);
const optionGroupValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
optionGroupValue.value = newVal;
},
);
const showIfComputed = computed(() => {
return props.showIf({
value: optionGroupValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
if (props.form && props.form.getStatus() === 'view') {
return true;
}
return props.disableIf({
value: optionGroupValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', value, props.form);
changeValue(value);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
</script>

143
io.sc.platform.core.frontend/src/platform/components/form/elements/WPassword.vue

@ -1,143 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-input
ref="pwdRef"
v-model="passwordValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="password"
autocomplete="off"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@update:model-value="updateModelValue"
@change="changeValue"
>
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, watch } from 'vue';
import { FormValidators } from '@/platform/components';
const pwdRef = ref();
const attrs = useAttrs();
const rules = attrs.rules;
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 passwordValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
passwordValue.value = newVal;
},
);
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (pwdRef?.value) {
pwdRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: passwordValue.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: passwordValue.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: passwordValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: passwordValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', value, props.form);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const validate = () => {
return pwdRef.value.validate();
};
defineExpose({
validate,
});
</script>

206
io.sc.platform.core.frontend/src/platform/components/form/elements/WPosition.vue

@ -1,206 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-input
ref="positionRef"
v-model="positionValueRef"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@update:model-value="positionChanged"
@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-pin-angle"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('select')"
:style="{ border: '1px solid #e5e7eb', 'background-color': computedStoreBackgroundColorValue }"
>
<q-popup-proxy v-model:model-value="isShow" anchor="bottom right" self="top right" :offset="[0, 10]">
<div class="row q-col-gutter-sm q-pa-sm" style="width: 150px">
<div v-for="position in positionList" :key="position" class="col-4">
<q-btn
unelevated
:title="$t(position)"
:color="positionValueRef == position ? 'primary' : ''"
:style="{
width: '100%',
border: positionValueRef == position ? '' : '1px solid #dddddd',
}"
@click="positionChanged(position)"
></q-btn>
</div>
</div>
</q-popup-proxy>
</q-btn>
<!-- 恢复默认值按钮 -->
<q-btn
v-if="restore"
icon="bi-arrow-counterclockwise"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('restore')"
:style="{ 'margin-left': '5px', border: '1px solid #e5e7eb', color: restoreValue, 'background-color': computedStoreBackgroundColorValue }"
@click="restoreDefaultValue"
></q-btn>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, toRaw, computed, useAttrs, watch } from 'vue';
import { Tools } from '@/platform';
import { FormValidators } from '@/platform/components';
const attrs = useAttrs();
const rules = attrs.rules;
const positionRef = ref();
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: String, default: '' },
restore: { type: Boolean, default: false },
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 positionList = ['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'];
const isShow = ref(false);
const positionValueRef = ref(props.modelValue || '');
const restoreValue = toRaw(props.modelValue || '');
watch(
() => props.modelValue,
(newVal, oldVal) => {
positionValueRef.value = newVal;
},
);
const computedStoreBackgroundColorValue = computed(() => {
const color: string = restoreValue;
if (color.toLowerCase() === 'white' || color.toLowerCase() === '#ffffff' || color.toLowerCase() === '#fff') {
return '#eee';
}
return undefined;
});
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (positionRef?.value) {
positionRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: positionValueRef.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: positionValueRef.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: positionValueRef.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: positionValueRef.value,
form: props.form,
});
});
const positionChanged = (value) => {
positionValueRef.value = value;
emit('update:modelValue', value, props.form);
isShow.value = false;
changeValue(value);
};
const restoreDefaultValue = () => {
positionValueRef.value = restoreValue;
emit('update:modelValue', restoreValue, props.form);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const validate = () => {
return positionRef.value.validate();
};
defineExpose({
validate,
});
</script>

7
io.sc.platform.core.frontend/src/platform/components/form/elements/WQueryCondition.vue

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

144
io.sc.platform.core.frontend/src/platform/components/form/elements/WSelect.vue

@ -1,144 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-select
ref="selectRef"
v-model="selectValue"
emit-value
map-options
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@update:model-value="updateModelValue"
@change="changeValue"
>
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template>
</q-select>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, watch } from 'vue';
import { FormValidators } from '@/platform/components';
const selectRef = ref();
const attrs = useAttrs();
const rules = attrs.rules;
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: [String, Number, Boolean, Array], 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 selectValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
selectValue.value = newVal;
},
);
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (selectRef?.value) {
selectRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: selectValue.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: selectValue.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: selectValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: selectValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', value, props.form);
changeValue(value);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const validate = () => {
return selectRef.value.validate();
};
defineExpose({
validate,
});
</script>

99
io.sc.platform.core.frontend/src/platform/components/form/elements/WSelectUserText.vue

@ -1,99 +0,0 @@
<template>
<div>
<q-input
v-show="!hideIfComputed"
ref="textRef"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="text"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
>
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs } from 'vue';
import { FormValidators } from '@/platform/components';
const textRef = ref();
const attrs = useAttrs();
const inRules = attrs.rules;
const props = defineProps({
hideIf: {
type: Function,
default: () => {
return false;
},
},
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 rules = inRules || <any>[];
if (!hideIfComputed.value && requiredIfComputed.value) {
rules.push(FormValidators.required());
} else if (hideIfComputed.value) {
rules = [];
}
if (textRef?.value) {
textRef.value.resetValidation();
}
return rules;
});
const hideIfComputed = computed(() => {
return props.hideIf(props.form);
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return props.requiredIf(props.form) || false;
} else if (props.required) {
return true;
}
return false;
});
const readonlyIfComputed = computed(() => {
return props.readOnlyIf(props.form);
});
const disableIfComputed = computed(() => {
return props.disableIf(props.form);
});
const validate = () => {
return textRef.value.validate();
};
defineExpose({
validate,
});
</script>

142
io.sc.platform.core.frontend/src/platform/components/form/elements/WText.vue

@ -1,142 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-input
ref="textRef"
v-model="textValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="text"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@update:model-value="updateModelValue"
@change="changeValue"
>
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, watch } from 'vue';
import { FormValidators } from '@/platform/components';
const textRef = ref();
const attrs = useAttrs();
const rules = attrs.rules;
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 textValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
textValue.value = newVal;
},
);
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (textRef?.value) {
textRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: textValue.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: textValue.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: textValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: textValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', value, props.form);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const validate = () => {
return textRef.value.validate();
};
defineExpose({
validate,
});
</script>

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

@ -1,155 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-input
ref="textBtnRef"
v-model="textBtnValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="text"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@focus="() => {}"
@update:model-value="updateModelValue"
@change="changeValue"
>
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template>
<template v-if="attrs.button && attrs.buttonPosition === 'prepend' && (!props.form || (props.form && props.form.getStatus() !== 'view'))" #prepend>
<q-btn round dense flat v-bind="attrs.button" @click="attrs.button.click" />
</template>
<template v-else-if="attrs.button && attrs.buttonPosition === 'before' && (!props.form || (props.form && props.form.getStatus() !== 'view'))" #before>
<q-btn round dense flat v-bind="attrs.button" @click="attrs.button.click" />
</template>
<template v-else-if="attrs.button && attrs.buttonPosition === 'after' && (!props.form || (props.form && props.form.getStatus() !== 'view'))" #after>
<q-btn round dense flat v-bind="attrs.button" @click="attrs.button.click" />
</template>
<template v-else-if="attrs.button && props.form && (!props.form || (props.form && props.form.getStatus() !== 'view'))" #append>
<q-btn round dense flat v-bind="attrs.button" @click="attrs.button.click" />
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, watch } from 'vue';
import { FormValidators } from '@/platform/components';
const textBtnRef = ref();
const attrs = useAttrs();
const rules = attrs.rules;
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 true;
},
},
disableIf: {
type: Function,
default: () => {
return false;
},
},
form: {
type: Object,
default: undefined,
},
});
const emit = defineEmits(['update:modelValue', 'change']);
const textBtnValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
textBtnValue.value = newVal;
},
);
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (textBtnRef?.value) {
textBtnRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: textBtnValue.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: textBtnValue.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: textBtnValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: textBtnValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', value, props.form);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const validate = () => {
return textBtnRef.value.validate();
};
defineExpose({
validate,
});
</script>

142
io.sc.platform.core.frontend/src/platform/components/form/elements/WTextarea.vue

@ -1,142 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-input
ref="textareaRef"
v-model="textareaValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="textarea"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@update:model-value="updateModelValue"
@change="changeValue"
>
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, watch } from 'vue';
import { FormValidators } from '@/platform/components';
const textareaRef = ref();
const attrs = useAttrs();
const rules = attrs.rules;
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 textareaValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
textareaValue.value = newVal;
},
);
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (textareaRef?.value) {
textareaRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: textareaValue.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: textareaValue.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: textareaValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: textareaValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', value, props.form);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const validate = () => {
return textareaRef.value.validate();
};
defineExpose({
validate,
});
</script>

154
io.sc.platform.core.frontend/src/platform/components/form/elements/WTreeSelect.vue

@ -1,154 +0,0 @@
<template>
<div v-show="showIfComputed">
<q-select
ref="selectRef"
v-model="selectValue"
emit-value
map-options
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@update:model-value="updateModelValue"
@change="changeValue"
>
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template>
<template #option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section avatar style="width: 30px">
<q-icon name="folder" color="amber" />
</q-item-section>
<q-item-section>
<q-item-label>{{ scope.opt.label }}</q-item-label>
</q-item-section>
</q-item>
</template>
</q-select>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, watch } from 'vue';
import { FormValidators } from '@/platform/components';
const selectRef = ref();
const attrs = useAttrs();
const rules = attrs.rules;
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: [String, Number, Boolean, Array], 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 selectValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newVal, oldVal) => {
selectValue.value = newVal;
},
);
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (selectRef?.value) {
selectRef.value.resetValidation();
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: selectValue.value,
form: props.form,
});
});
const requiredIfComputed = computed(() => {
if (props.requiredIf) {
return (
props.requiredIf({
value: selectValue.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: selectValue.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: selectValue.value,
form: props.form,
});
});
const updateModelValue = (value) => {
emit('update:modelValue', value, props.form);
changeValue(value);
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
};
const validate = () => {
return selectRef.value.validate();
};
defineExpose({
validate,
});
</script>

38
io.sc.platform.core.frontend/src/platform/components/grid/GridBody.vue

@ -15,6 +15,7 @@
:get-row-component-refs="getRowComponentRefs"
:set-old-value="setOldValue"
:no-data-tr-colspan="noDataTrColspan"
:updates="updates"
></TreeGridRow>
</template>
<template v-else>
@ -65,7 +66,7 @@
</template>
<script setup lang="ts">
import { computed, inject, ref, toRaw } from 'vue';
import { Tools } from '@/platform';
import { Tools, noErrorAxios, NotifyManager, t } from '@/platform';
import { draggableImage } from './ts/grid';
import TreeGridRow from './TreeGridRow.vue';
import GridTd from './GridTd.vue';
@ -201,6 +202,37 @@ const gridTrMiddleHeightComputed = computed(() => {
}
});
//
const updates = (data, callback) => {
const requestParams = {
method: 'PUT',
headers: { 'content-type': 'application/json;charset=utf-8;' },
data: data,
url: props.url.dataUrl + '/updates',
};
noErrorAxios(requestParams)
.then((resp) => {
if (!Tools.isEmpty(callback)) {
callback(resp?.data);
}
})
.catch((error) => {
const response = error?.response;
const status = response?.status;
const data = response?.data;
if (data?.code === 1001) {
NotifyManager.error('服务器验证未通过');
} else {
//
if (status === 500) {
NotifyManager.error(t(data?.errorMessageI18nKey));
} else {
NotifyManager.error(t(status));
}
}
});
};
//
const onDragStart = (e, scope) => {
const img = new Image();
@ -298,7 +330,7 @@ const onDrop = (e, scope) => {
});
if (typeof props.grid.props.draggable === 'boolean' && props.grid.props.draggable) {
// 访
props.grid.updates(updateData);
updates(updateData, () => {});
}
props.afterRowDraggable(updateData);
@ -309,7 +341,7 @@ const updateTicked = (evt: Event, row: any) => {
props.getRow(table.rows, row[props.rowKeyName], false)[table.selectedField] = row[table.tickedField];
props.allTickedStatus();
if (props.grid.props.onUpdateTicked) {
props.grid.emit('updateTicked', evt, row);
props.grid.emit('updateTicked', { grid: props.grid, evt, row });
}
} else {
props.getRow(table.rows, row[props.rowKeyName], false)[table.tickedField] = !props.getRow(table.rows, row[props.rowKeyName], false)[table.tickedField];

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

@ -271,6 +271,6 @@ const showColumn = (name) => {
};
const denseChange = () => {
props.grid.stickyHeaderColumn(0);
props.grid.refreshStyle(0);
};
</script>

55
io.sc.platform.core.frontend/src/platform/components/grid/GridEditToolbar.vue

@ -132,17 +132,21 @@ const rowSave = (args) => {
let localeUpdateFlag = false;
let url = '';
//
props.grid.emit('beforeEditorDataSubmit', data, (handlerRequestParams: any | boolean, localeUpdate: boolean = false) => {
props.grid.emit('beforeEditorDataSubmit', {
grid: props.grid,
data: data,
callback: (handlerRequestParams: any | boolean, localeUpdate: boolean = false) => {
if (typeof handlerRequestParams === 'boolean' && handlerRequestParams === false) {
submitFlag = false;
} else {
data = handlerRequestParams;
}
localeUpdateFlag = localeUpdate;
},
});
if (localeUpdateFlag && submitFlag) {
// 访
props.grid.replaceRow(data);
props.grid.updateLocalData(data);
table.bodyEditStatus = 'none';
} else {
if (submitFlag) {
@ -160,12 +164,12 @@ const rowSave = (args) => {
};
noErrorAxios(requestParams)
.then((resp) => {
props.grid.emit('afterEditorDataSubmit', resp.data);
props.grid.emit('afterEditorDataSubmit', { grid: props.grid, data: resp.data });
NotifyManager.info(t('tip.operationSuccess'));
if (props.grid.props.refreshData || !props.grid.props.tree) {
props.grid.refresh();
} else if (resp.data) {
props.grid.replaceRow(data);
props.grid.updateLocalData(data);
}
// 退
table.bodyEditStatus = 'none';
@ -229,29 +233,33 @@ const rowsSave = (args) => {
let submitFlag = true;
let localeUpdateFlag = false;
//
props.grid.emit('beforeEditorDataSubmit', data, (handlerRequestParams: any | boolean, localeUpdate: boolean = false) => {
props.grid.emit('beforeEditorDataSubmit', {
grid: props.grid,
data: data,
callback: (handlerRequestParams: any | boolean, localeUpdate: boolean = false) => {
if (typeof handlerRequestParams === 'boolean' && handlerRequestParams === false) {
submitFlag = false;
} else {
data = handlerRequestParams;
}
localeUpdateFlag = localeUpdate;
},
});
if (localeUpdateFlag && submitFlag) {
// 访
data.forEach((item) => {
props.grid.replaceRow(item);
props.grid.updateLocalData(item);
});
table.bodyEditStatus = 'none';
} else {
if (submitFlag) {
props.grid.updates(data, (callbackData) => {
updates(data, (callbackData) => {
NotifyManager.info(t('tip.operationSuccess'));
if (props.grid.props.refreshData || !props.grid.props.tree) {
props.grid.refresh();
} else if (!Tools.isEmpty(callbackData)) {
callbackData.forEach((item) => {
props.grid.replaceRow(item);
props.grid.updateLocalData(item);
});
}
// 退
@ -275,6 +283,37 @@ const save = async (args) => {
}
};
//
const updates = (data, callback) => {
const requestParams = {
method: 'PUT',
headers: { 'content-type': 'application/json;charset=utf-8;' },
data: data,
url: props.url.dataUrl + '/updates',
};
noErrorAxios(requestParams)
.then((resp) => {
if (!Tools.isEmpty(callback)) {
callback(resp?.data);
}
})
.catch((error) => {
const response = error?.response;
const status = response?.status;
const data = response?.data;
if (data?.code === 1001) {
NotifyManager.error('服务器验证未通过');
} else {
//
if (status === 500) {
NotifyManager.error(t(data?.errorMessageI18nKey));
} else {
NotifyManager.error(t(status));
}
}
});
};
const showRowEditButtonComputed = computed(() => {
if (table.bodyEditStatus === 'rowEdit' && isSelectedRowComputed.value) {
return true;

12
io.sc.platform.core.frontend/src/platform/components/grid/GridEditor.vue

@ -59,13 +59,17 @@ const save = async () => {
}
let submitFlag = true;
let closeDialog = true;
props.grid.emit('beforeEditorDataSubmit', dialogFormData, (handlerRequestParams: any | boolean, closeFlag: boolean = true) => {
props.grid.emit('beforeEditorDataSubmit', {
grid: props.grid,
data: dialogFormData,
callback: (handlerRequestParams: any | boolean, closeFlag: boolean = true) => {
if (typeof handlerRequestParams === 'boolean' && handlerRequestParams === false) {
submitFlag = false;
} else {
dialogFormData = handlerRequestParams;
}
closeDialog = closeFlag;
},
});
if (submitFlag) {
if (formStatus === 'addTop') {
@ -91,7 +95,7 @@ const save = async () => {
noErrorAxios(requestParams)
.then((resp) => {
dialog.dialogButtons[0].loading = false;
props.grid.emit('afterEditorDataSubmit', resp.data);
props.grid.emit('afterEditorDataSubmit', { grid: props.grid, data: resp.data });
NotifyManager.info(t('tip.operationSuccess'));
if (closeDialog) {
dialogRef.value.hide();
@ -170,7 +174,7 @@ const addData = (rowData) => {
addTreeRow(rowData);
props.setRowDataExtraProperty(table.rows);
} else {
props.grid.addRow(rowData, false);
props.grid.addLocalData(rowData, false);
}
};
const updateData = (rowData) => {
@ -180,7 +184,7 @@ const updateData = (rowData) => {
if (selected['children']) {
rowData['children'] = selected['children'];
}
props.grid.replaceRow(rowData);
props.grid.updateLocalData(rowData);
};
const getMethod = (formStatus: string) => {

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

@ -188,7 +188,7 @@ const allTickedUpdateFun = (value, evt) => {
} else if (table.bodyEditStatus === 'rowsEdit') {
table.allTicked = false;
}
props.grid.emit('updateTickeds', value);
props.grid.emit('updateTickeds', { grid: props.grid, value: value });
};
const handlerStickyChildrenColumn = (item, columns) => {

62
io.sc.platform.core.frontend/src/platform/components/grid/GridTop.vue

@ -157,7 +157,7 @@ const edit = (selected) => {
props.grid.getEditorDialog().setTitle(t('action.edit'));
props.grid.getEditorForm().setStatus('edit');
props.grid.getEditorForm().setData(selected);
props.grid.emit('afterEditorOpen', selected);
props.grid.emit('afterEditorOpen', { grid: props.grid, data: selected });
});
}
};
@ -172,7 +172,7 @@ const clone = (selected) => {
props.grid.getEditorDialog().setTitle(t('action.copy'));
props.grid.getEditorForm().setStatus('clone');
props.grid.getEditorForm().setData(selected);
props.grid.emit('afterEditorOpen', selected);
props.grid.emit('afterEditorOpen', { grid: props.grid, data: selected });
});
}
};
@ -195,20 +195,35 @@ const remove = () => {
url: props.url.removeDataUrl || props.url.dataUrl,
data: ids,
};
let flag = true;
props.grid.emit('beforeRemove', {
grid: props.grid,
ids: ids,
callback: (param: any) => {
if (param && Array.isArray(param)) {
ids.splice(0, ids.length - 1);
ids.push(...param);
} else {
flag = false;
}
},
});
if (flag) {
axios(requestParams)
.then((resp) => {
props.grid.emit('afterRemove', resp?.data);
props.grid.emit('afterRemove', { grid: props.grid, ids: resp?.data });
NotifyManager.info(t('tip.operationSuccess'));
if (props.grid.props.refreshData || !props.grid.props.tree) {
props.grid.refresh();
} else {
props.grid.removeRows(resp?.data);
props.grid.removeLocalData(resp?.data);
}
})
.catch((error) => {
console.error(error);
console.info('==========error==========', error);
});
}
};
const getExportData = async () => {
@ -322,7 +337,7 @@ const buttonObj = reactive({
nextTick(() => {
props.grid.getEditorDialog().setTitle(t('action.addNew'));
props.grid.getEditorForm().setStatus('add');
props.grid.emit('afterEditorOpen');
props.grid.emit('afterEditorOpen', { grid: props.grid, data: undefined });
});
},
},
@ -352,7 +367,7 @@ const buttonObj = reactive({
}
return false;
},
click: (args) => {
click: () => {
table.bodyEditStatus = 'rowEdit';
},
},
@ -361,7 +376,7 @@ const buttonObj = reactive({
icon: 'app_registration',
labelI18nKey: 'action.edit',
label: t('action.edit'),
click: (args) => {
click: () => {
table.bodyEditStatus = 'rowsEdit';
//
props.grid.cleanSelected();
@ -380,8 +395,8 @@ const buttonObj = reactive({
}
return false;
},
click: (args) => {
clone(args.selected);
click: () => {
clone(props.grid.getSelectedRow());
},
},
remove: {
@ -475,7 +490,7 @@ const buttonObj = reactive({
nextTick(() => {
props.grid.getEditorDialog().setTitle(t('action.addTop'));
props.grid.getEditorForm().setStatus('addTop');
props.grid.emit('afterEditorOpen');
props.grid.emit('afterEditorOpen', { grid: props.grid, data: undefined });
});
},
},
@ -495,20 +510,24 @@ const buttonObj = reactive({
nextTick(() => {
props.grid.getEditorDialog().setTitle(t('action.addChild'));
props.grid.getEditorForm().setStatus('addChild');
props.grid.emit('afterEditorOpen');
props.grid.emit('afterEditorOpen', { grid: props.grid, data: undefined });
});
},
},
expand: {
name: 'expand',
icon: (args) => {
icon: () => {
return table.treeExpand ? 'expand_less' : 'expand_more';
},
label: (args) => {
label: () => {
return table.treeExpand ? t('action.collapseAll') : t('action.expandAll');
},
click: () => {
props.grid.expandFun(table.rows, table.treeExpand);
if (table.treeExpand) {
props.grid.collapse();
} else {
props.grid.expand();
}
table.treeExpand = !table.treeExpand;
},
},
@ -642,12 +661,25 @@ onBeforeMount(() => {
defineExpose({
setLocaleFlag,
edit,
handlerQueryFormShowField,
handleToolbarActions,
resetLabel,
dbClickOperation,
getQueryForm,
reset: buttonObj.reset.click,
add: buttonObj.add.click,
edit,
rowEdit: buttonObj.rowEdit.click,
rowsEdit: buttonObj.rowsEdit.click,
clone: buttonObj.clone.click,
remove: buttonObj.remove.click,
view: buttonObj.view.click,
export: buttonObj.export.click,
addTop: buttonObj.addTop.click,
addChild: buttonObj.addChild.click,
expand: buttonObj.expand.click,
resetDefaultValues: buttonObj.resetDefaultValues.click,
});
</script>

11
io.sc.platform.core.frontend/src/platform/components/grid/TreeGridRow.vue

@ -123,6 +123,7 @@
:get-row-component-refs="props.getRowComponentRefs"
:set-old-value="props.setOldValue"
:no-data-tr-colspan="props.noDataTrColspan"
:updates="props.updates"
></TreeGridRow>
</template>
</template>
@ -200,6 +201,10 @@ const props = defineProps({
return 0;
},
},
updates: {
type: Function,
default: () => {},
},
});
const table = inject('table');
@ -398,7 +403,7 @@ const onDragStart = (e, dataRow) => {
) {
table.dragRecords = selecteds;
} else {
props.grid.cleanSelected();
props.grid.clearSelected();
//
dataRow[table.selectedField] = true;
table.dragRecords = [dataRow];
@ -666,7 +671,7 @@ const onDrop = (e, dataRow) => {
//
if (typeof props.grid.props.draggable === 'boolean' && props.grid.props.draggable && updateOrderData?.length > 0) {
props.grid.updates(updateOrderData);
props.updates(updateOrderData);
}
props.afterRowDraggable(updateOrderData);
@ -675,7 +680,7 @@ const onDrop = (e, dataRow) => {
const click = (evt, row, rowIndex) => {
if (table.bodyEditStatus === 'none') {
if (!evt.ctrlKey) {
props.grid.cleanSelected();
props.grid.clearSelected();
}
row[table.selectedField] = true;
if (props.grid.props.onRowClick) {

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

File diff suppressed because it is too large

6
io.sc.platform.core.frontend/src/platform/components/grid/ts/grid.ts

@ -1,4 +1,4 @@
import { Tools, t, componentRegistryName } from '@/platform';
import { Tools, t } from '@/platform';
// 列样式处理
const columnStyle = (item: any) => {
@ -30,10 +30,6 @@ const childrenHandler = (item: any, gridColumns: any) => {
});
} else {
columnStyle(item);
// 替换行编辑类型为注册名
if (!Tools.isEmpty(item.type)) {
item.type = componentRegistryName(item.type);
}
const col = {
...{ align: 'left', label: item.name, field: item.name, name: item.name, sortable: true, hidden: false },
...item,

225
io.sc.platform.core.frontend/src/platform/components/form/elements/WIcon.vue → io.sc.platform.core.frontend/src/platform/components/icon/WIcon.vue

@ -1,24 +1,24 @@
<template>
<div v-show="showIfComputed">
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="iconRef"
v-model="valueRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="rulesComputed"
:readonly="readonlyIfComputed"
:disable="disableIfComputed"
@update:model-value="updateModelValue"
@change="changeValue"
:rules="FieldMethods.getRules(props, modelValue, iconRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
<template #prepend>
<q-icon v-if="valueRef" :name="valueRef"> </q-icon>
<q-icon v-if="modelValue" :name="modelValue"> </q-icon>
<w-icon-empty v-else></w-icon-empty>
</template>
<template v-if="!props.form || (props.form && props.form.getStatus() !== 'view')" #append>
<template v-if="!props['form'] || (props['form'] && props['form'].getStatus() !== 'view')" #append>
<q-btn icon="bi-search" size="10px" 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]" @show="show">
<q-splitter v-model="leftWidthRef" style="width: 800px; height: 300px; position: relative">
@ -57,7 +57,7 @@
class="grid justify-items-center q-pa-sm"
:title="item"
:style="
item === valueRef
item === modelValue
? 'background-color:var(--q-negative);'
: searchRef && item.indexOf(searchRef) !== -1
? 'background-color:var(--q-warning);'
@ -84,7 +84,7 @@
square
unelevated
:title="$t('restore')"
:style="{ 'margin-left': '5px', border: '1px solid #e5e7eb', color: restoreValue, 'background-color': computedStoreBackgroundColorValue }"
:style="{ 'margin-left': '5px', border: '1px solid #e5e7eb', color: restoreValue }"
@click="restoreDefaultValue"
></q-btn>
</template>
@ -92,9 +92,10 @@
</div>
</template>
<script setup lang="ts">
import { ref, toRaw, computed, useAttrs, watch } from 'vue';
import { ref, toRaw, computed, useAttrs, onBeforeMount } from 'vue';
import { Tools } from '@/platform';
import { FormValidators } from '@/platform/components';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
import bootstrap from './icons/bootstrap.json';
import materialIcons from './icons/material-icons.json';
import materialSymbolsOutlined from './icons/material-symbols-outlined.json';
@ -103,176 +104,104 @@ import fontawesomeBrands from './icons/fontawesome-v6-icons-brands.json';
import fontawesomeRegular from './icons/fontawesome-v6-icons-regular.json';
import fontawesomeSolid from './icons/fontawesome-v6-icons-solid.json';
const attrs = useAttrs();
const rules = attrs.rules;
const iconRef = ref();
const props = defineProps({
onChange: {
type: Function,
default: () => {},
},
modelValue: { type: String, default: '' },
restore: { type: Boolean, default: false },
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 iconSets = [
{ name: 'bootstrap', label: 'Bootstrap', prefix: 'bi-', icons: bootstrap },
{ name: 'materialIcons', label: 'Material', prefix: '', icons: materialIcons },
{ name: 'materialSymbolsOutlined', label: 'Material Symbols (outlined)', prefix: 'sym_o_', icons: materialSymbolsOutlined },
{ name: 'eva', label: 'Eva', prefix: 'eva-', icons: eva },
{ name: 'fontawesomeBrands', label: 'fontawesome (brands)', prefix: 'fa-brands fa-', icons: fontawesomeBrands },
{ name: 'fontawesomeRegular', label: 'fontawesome (regular)', prefix: 'fa-regular fa-', icons: fontawesomeRegular },
{ name: 'fontawesomeSolid', label: 'fontawesome (solid)', prefix: 'fa-solid fa-', icons: fontawesomeSolid },
];
const attrs = useAttrs();
const modelValue = defineModel<string>();
const leftWidthRef = ref(30);
const selectedTab = ref('bootstrap');
const isShow = ref(false);
const searchRef = ref('');
const valueRef = ref(props.modelValue || 'bi-arrow-up-left-square');
if (Tools.isEmpty(props.modelValue)) {
emit('update:modelValue', valueRef.value, props.form);
}
watch(
() => props.modelValue,
(newVal, oldVal) => {
valueRef.value = newVal;
},
);
const restoreValue = toRaw(props.modelValue || '');
const defaultValue = 'bi-arrow-up-left-square';
const restoreValue = toRaw(modelValue.value || defaultValue);
const rulesComputed = computed(() => {
let result = rules || <any>[];
if (showIfComputed.value && requiredIfComputed.value) {
result.push(FormValidators.required());
} else if (!showIfComputed.value) {
result = [];
}
if (iconRef?.value) {
iconRef.value.resetValidation();
interface FieldProps extends FormFieldProps {
restore?: boolean;
}
return result;
});
const showIfComputed = computed(() => {
return props.showIf({
value: valueRef.value,
form: props.form,
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
restore: false,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: 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;
isShow.value = false;
};
validate = () => {
return iconRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = restoreValue;
};
}
return props.readOnlyIf({
value: valueRef.value,
form: props.form,
});
});
const disableIfComputed = computed(() => {
return props.disableIf({
value: valueRef.value,
form: props.form,
});
});
const fieldMethodsClass = new FieldMethods();
const iconSets = [
{ name: 'bootstrap', label: 'Bootstrap', prefix: 'bi-', icons: bootstrap },
{ name: 'materialIcons', label: 'Material', prefix: '', icons: materialIcons },
{ name: 'materialSymbolsOutlined', label: 'Material Symbols (outlined)', prefix: 'sym_o_', icons: materialSymbolsOutlined },
{ name: 'eva', label: 'Eva', prefix: 'eva-', icons: eva },
{ name: 'fontawesomeBrands', label: 'fontawesome (brands)', prefix: 'fa-brands fa-', icons: fontawesomeBrands },
{ name: 'fontawesomeRegular', label: 'fontawesome (regular)', prefix: 'fa-regular fa-', icons: fontawesomeRegular },
{ name: 'fontawesomeSolid', label: 'fontawesome (solid)', prefix: 'fa-solid fa-', icons: fontawesomeSolid },
];
if (Tools.isEmpty(valueRef.value)) {
if (Tools.isEmpty(modelValue.value)) {
selectedTab.value = 'bootstrap';
} else if (valueRef.value.startsWith('bi')) {
} else if (modelValue.value?.startsWith('bi')) {
selectedTab.value = 'bootstrap';
} else if (valueRef.value.startsWith('sym_o_')) {
} else if (modelValue.value?.startsWith('sym_o_')) {
selectedTab.value = 'materialSymbolsOutlined';
} else if (valueRef.value.startsWith('eva')) {
} else if (modelValue.value?.startsWith('eva')) {
selectedTab.value = 'eva';
} else if (valueRef.value.startsWith('fa-brands fa-')) {
} else if (modelValue.value?.startsWith('fa-brands fa-')) {
selectedTab.value = 'fontawesomeBrands';
} else if (valueRef.value.startsWith('fa-regular fa-')) {
} else if (modelValue.value?.startsWith('fa-regular fa-')) {
selectedTab.value = 'fontawesomeRegular';
} else if (valueRef.value.startsWith('fa-solid fa-')) {
} else if (modelValue.value?.startsWith('fa-solid fa-')) {
selectedTab.value = 'fontawesomeSolid';
} else {
selectedTab.value = 'materialIcons';
}
const iconItemClick = (icon: string) => {
valueRef.value = icon;
isShow.value = false;
updateModelValue(icon);
changeValue(icon);
};
const updateModelValue = (value) => {
valueRef.value = value;
emit('update:modelValue', value, props.form);
isShow.value = false;
};
const changeValue = (value) => {
emit('change', {
value: value,
form: props.form,
});
modelValue.value = icon;
fieldMethodsClass.updateValue(icon);
};
const restoreDefaultValue = () => {
valueRef.value = restoreValue;
emit('update:modelValue', restoreValue, props.form);
modelValue.value = restoreValue;
fieldMethodsClass.updateValue(restoreValue);
};
const show = () => {
if (!Tools.isEmpty(modelValue.value)) {
searchRef.value = '';
document.getElementById(valueRef.value)?.scrollIntoView(true);
document.getElementById(modelValue.value)?.scrollIntoView(true);
}
};
const validate = () => {
return iconRef.value.validate();
};
onBeforeMount(() => {
if (Tools.isEmpty(modelValue.value)) {
modelValue.value = defaultValue;
}
});
defineExpose({
validate,
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/bootstrap.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/bootstrap.json

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/eva-icons.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/eva-icons.json

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/fontawesome-v6-icons-brands.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/fontawesome-v6-icons-brands.json

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/fontawesome-v6-icons-regular.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/fontawesome-v6-icons-regular.json

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/fontawesome-v6-icons-solid.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/fontawesome-v6-icons-solid.json

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/material-icons-outlined.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/material-icons-outlined.json

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/material-icons-rounded.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/material-icons-rounded.json

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/material-icons-sharp.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/material-icons-sharp.json

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/material-icons.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/material-icons.json

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/material-symbols-outlined.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/material-symbols-outlined.json

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/material-symbols-rounded.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/material-symbols-rounded.json

0
io.sc.platform.core.frontend/src/platform/components/form/elements/icons/material-symbols-sharp.json → io.sc.platform.core.frontend/src/platform/components/icon/icons/material-symbols-sharp.json

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

@ -7,23 +7,29 @@ import WDialog from './dialog/WDialog.vue';
import WDrawer from './drawer/WDrawer.vue';
import WForm from './form/WForm.vue';
import WCodeMirror from './form/elements/WCodeMirror.vue';
import WColorInput from './form/elements/WColorInput.vue';
import WColorInputPalette from './form/elements/WColorInputPalette.vue';
import WCron from './form/elements/WCron.vue';
import WIcon from './form/elements/WIcon.vue';
import WPosition from './form/elements/WPosition.vue';
import WText from './form/elements/WText.vue';
import WNumber from './form/elements/WNumber.vue';
import WTextarea from './form/elements/WTextarea.vue';
import WSelect from './form/elements/WSelect.vue';
import WTreeSelect from './form/elements/WTreeSelect.vue';
import WDate from './form/elements/WDate.vue';
import WCheckbox from './form/elements/WCheckbox.vue';
import WTextBtn from './form/elements/WTextBtn.vue';
import WPassword from './form/elements/WPassword.vue';
import WOptionGroup from './form/elements/WOptionGroup.vue';
import WFile from './form/elements/WFile.vue';
import WCodeMirror from './code-mirror/WCodeMirror.vue';
import WColorInput from './color/WColorInput.vue';
import WColorInputPalette from './color/WColorInputPalette.vue';
import WCron from './cron/WCron.vue';
import WIcon from './icon/WIcon.vue';
import WPosition from './position/WPosition.vue';
import WText from './text/WText.vue';
import WNumber from './number/WNumber.vue';
import WTextarea from './textarea/WTextarea.vue';
import WSelect from './select/WSelect.vue';
import WInputSelect from './select/WInputSelect.vue';
import WGridSelect from './select/WGridSelect.vue';
import WUserSelect from './select/WUserSelect.vue';
import WOrgSelect from './select/WOrgSelect.vue';
import WDate from './date/WDate.vue';
import WDateRange from './date/WDateRange.vue';
import WCheckbox from './checkbox/WCheckbox.vue';
import WCheckboxGroup from './checkbox/WCheckboxGroup.vue';
import WPassword from './password/WPassword.vue';
import WFile from './file/WFile.vue';
import WLabel from './label/WLabel.vue';
import WRadio from './radio/WRadio.vue';
import WTextEditor from './text-editor/WTextEditor.vue';
import WGrid from './grid/WGrid.vue';
@ -74,13 +80,19 @@ export default {
app.component('WNumber', WNumber);
app.component('WTextarea', WTextarea);
app.component('WSelect', WSelect);
app.component('WTreeSelect', WTreeSelect);
app.component('WInputSelect', WInputSelect);
app.component('WGridSelect', WGridSelect);
app.component('WUserSelect', WUserSelect);
app.component('WOrgSelect', WOrgSelect);
app.component('WDate', WDate);
app.component('WDateRange', WDateRange);
app.component('WCheckbox', WCheckbox);
app.component('WTextBtn', WTextBtn);
app.component('WCheckboxGroup', WCheckboxGroup);
app.component('WPassword', WPassword);
app.component('WOptionGroup', WOptionGroup);
app.component('WFile', WFile);
app.component('WLabel', WLabel);
app.component('WRadio', WRadio);
app.component('WTextEditor', WTextEditor);
app.component('WGrid', WGrid);
@ -128,12 +140,13 @@ export {
WNumber,
WTextarea,
WSelect,
WTreeSelect,
WGridSelect,
WUserSelect,
WOrgSelect,
WLabel,
WDate,
WCheckbox,
WTextBtn,
WPassword,
WOptionGroup,
WFile,
WGrid,
WIconEmpty,

7
io.sc.platform.core.frontend/src/platform/components/label/WLabel.vue

@ -0,0 +1,7 @@
<template><span v-if="props.required" style="color: red">*</span> {{ props.label }}</template>
<script setup lang="ts">
const props = defineProps({
required: { type: Boolean, default: false },
label: { type: String, default: '' },
});
</script>

75
io.sc.platform.core.frontend/src/platform/components/number/WNumber.vue

@ -0,0 +1,75 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="numberRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="number"
title=""
:rules="FieldMethods.getRules(props, modelValue, numberRef, [FormValidators.maxPrecision(props.precision)])"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { FormValidators } from '@/platform/components';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const numberRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<number>();
interface FieldProps extends FormFieldProps {
// FieldPropspropsdefineProps
precision?: number;
}
const props = withDefaults(defineProps<FieldProps>(), { precision: 0, showIf: true }); // 使withDefaults
class FieldMethods extends FormFieldMethods {
// FieldMethods
updateValue = (value_) => {
const floatValue = parseFloat(value_);
if (!isNaN(floatValue)) {
modelValue.value = floatValue;
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: floatValue,
form: props['form'],
});
}
} else {
modelValue.value = undefined;
}
};
validate = () => {
return numberRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
//
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

66
io.sc.platform.core.frontend/src/platform/components/password/WPassword.vue

@ -0,0 +1,66 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="pwdRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="password"
autocomplete="off"
:rules="FieldMethods.getRules(props, modelValue, pwdRef, undefined)"
::readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const pwdRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string>();
interface FieldProps extends FormFieldProps {}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return pwdRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

141
io.sc.platform.core.frontend/src/platform/components/position/WPosition.vue

@ -0,0 +1,141 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="positionRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="FieldMethods.getRules(props, modelValue, positionRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
<template v-if="!props['form'] || (props['form'] && props['form'].getStatus() !== 'view')" #append>
<!-- 选择位置按钮 -->
<q-btn
icon="bi-pin-angle"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('select')"
:style="{ border: '1px solid #e5e7eb', 'background-color': computedStoreBackgroundColorValue }"
>
<q-popup-proxy v-model:model-value="isShow" anchor="bottom right" self="top right" :offset="[0, 10]">
<div class="row q-col-gutter-sm q-pa-sm" style="width: 150px">
<div v-for="position in positionList" :key="position" class="col-4">
<q-btn
unelevated
:title="$t(position)"
:color="modelValue == position ? 'primary' : ''"
:style="{
width: '100%',
border: modelValue == position ? '' : '1px solid #dddddd',
}"
@click="fieldMethodsClass.updateValue(position)"
></q-btn>
</div>
</div>
</q-popup-proxy>
</q-btn>
<!-- 恢复默认值按钮 -->
<q-btn
v-if="restore"
icon="bi-arrow-counterclockwise"
size="10px"
padding="2px"
flat
square
unelevated
:title="$t('restore')"
:style="{ 'margin-left': '5px', border: '1px solid #e5e7eb', color: restoreValue, 'background-color': computedStoreBackgroundColorValue }"
@click="restoreDefaultValue"
></q-btn>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, toRaw, computed, useAttrs, onBeforeMount } from 'vue';
import { Tools } from '@/platform';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const positionRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string>();
const positionList = ['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'];
const isShow = ref(false);
const defaultValue = 'center';
const restoreValue = toRaw(modelValue.value || defaultValue);
interface FieldProps extends FormFieldProps {
restore?: boolean;
}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
restore: false,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
isShow.value = false;
modelValue.value = value_;
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return positionRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = restoreValue;
};
}
const fieldMethodsClass = new FieldMethods();
const computedStoreBackgroundColorValue = computed(() => {
const color: string = restoreValue;
if (color.toLowerCase() === 'white' || color.toLowerCase() === '#ffffff' || color.toLowerCase() === '#fff') {
return '#eee';
}
return undefined;
});
const restoreDefaultValue = () => {
modelValue.value = restoreValue;
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: restoreValue,
form: props['form'],
});
}
};
onBeforeMount(() => {
if (Tools.isEmpty(modelValue.value)) {
modelValue.value = defaultValue;
}
});
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

119
io.sc.platform.core.frontend/src/platform/components/radio/WRadio.vue

@ -0,0 +1,119 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)" :class="props.simple ? 'w-radio' : ''">
<q-field
ref="radioRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
v-bind="attrs"
:outlined="props.outlined"
:borderless="!props.outlined"
:stack-label="true"
:dense="true"
:clearable="false"
:rules="FieldMethods.getRules(props, modelValue, radioRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
<template v-if="!props.simple && !Tools.isEmpty(modelValue) && props.clearable" #append>
<q-btn flat square unelevated dense icon="cancel" @click="fieldMethodsClass.clearValue"></q-btn>
</template>
<template v-else-if="!props.simple" #control>
<div class="pt-[5px]">
<q-option-group
v-model="modelValue"
:dense="props.dense"
:options="props.options"
:inline="props.inline"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
/>
</div>
</template>
<template v-else #append>
<q-option-group
v-model="modelValue"
:dense="props.dense"
:options="props.options"
:inline="props.inline"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
/>
<q-btn v-if="!Tools.isEmpty(modelValue) && props.clearable" flat square unelevated dense icon="cancel" @click="fieldMethodsClass.clearValue"></q-btn>
</template>
</q-field>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { Tools } from '@/platform';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const radioRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string | boolean | number>();
interface FieldProps extends FormFieldProps {
clearable?: boolean;
dense?: boolean;
inline?: boolean;
outlined?: boolean;
borderless?: boolean;
simple?: boolean; // label使
options: Array<() => void>;
}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
clearable: true,
dense: true,
inline: true,
outlined: true,
simple: true,
options: () => {
return [];
},
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return radioRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>
<style lang="css">
.w-radio .q-field--dense .q-field__label {
font-size: 18px;
top: 18px;
}
.w-radio .q-field__marginal {
font-size: 16px;
}
</style>

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

@ -0,0 +1,272 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="textSelectRef"
v-model="displayValueComputed"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
:autogrow="true"
v-bind="attrs"
:bottom-slots="counter"
type="text"
:rules="FieldMethods.getRules(props, { value: modelValue, displayValue: displayValueComputed }, textSelectRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, { value: modelValue, displayValue: displayValueComputed })"
:disable="FieldMethods.getDisable(props, { value: modelValue, displayValue: displayValueComputed })"
:clearable="false"
@update:model-value="fieldMethodsClass.updateValue"
@focus="
() => {
textSelectRef?.blur();
}
"
>
<!-- label -->
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
<template v-if="!props['form'] || (props['form'] && props['form'].getStatus() !== 'view')" #append>
<q-btn v-if="!Tools.isEmpty(displayValueComputed)" flat square unelevated dense icon="cancel" @click="fieldMethodsClass.clearValue"></q-btn>
<q-btn flat square unelevated dense icon="manage_search">
<q-popup-proxy v-model:model-value="isShow" anchor="bottom right" self="top right" :offset="[0, 10]">
<div style="width: 700px; height: 300px">
<w-grid
ref="gridRef"
v-bind="props.grid"
db-click-operation="none"
:checkbox-selection="props.multiple || false"
:dense="true"
:config-button="false"
:ticked-record="{
columnName: valueUseColumnName,
data: typeof modelValue === 'string' ? [modelValue] : modelValue,
}"
@row-click="rowClick"
@update-ticked="updateTicked"
@update-tickeds="updateTickeds"
>
</w-grid>
</div>
</q-popup-proxy>
</q-btn>
</template>
<template v-if="counter" #counter>
<div>{{ modelValue?.length }}</div>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, onBeforeMount, toRaw } from 'vue';
import { Tools, axios } from '@/platform';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const textSelectRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string | Array<string>>();
const modelObjectValue = ref(<any>[]); //
const gridRef = ref();
const isShow = ref(false);
interface FieldProps extends FormFieldProps {
multiple?: boolean;
counter?: boolean;
displayValue: string | ((args: object) => '');
grid: object; //
}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
multiple: false, //
counter: false, //
/**
* 显示值配置必须传入可使用配置
* 列名填写列名
* 自定义函数自己定义显示的内容组件会将用户数据传到函数中函数必须返回一个字符串示例
* displayValue: (args) => {
* return args['data']['name'] + '_' + args['data']['code'];
* }
*/
displayValue: '',
});
class FieldMethods extends FormFieldMethods {
//
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
displayValue: displayValueComputed.value,
form: props['form'],
});
}
};
//
validate = () => {
return textSelectRef.value.validate();
};
//
setValue = (value) => {
if (props.multiple && Array.isArray(value) && Array.isArray(modelValue.value)) {
fieldMethodsClass.clearValue();
modelValue.value.push(...value);
setObjectValueByValue(value);
} else if (!props.multiple && !Array.isArray(value)) {
modelValue.value = value;
setObjectValueByValue(value);
} else {
console.info('error========模型值不匹配');
}
};
//
getValue = () => {
return modelValue.value;
};
//
getObjectValue = () => {
return modelObjectValue.value;
};
//
clearValue = () => {
if (props.multiple && Array.isArray(modelValue.value)) {
modelValue.value.splice(0, modelValue.value.length);
} else {
modelValue.value = undefined;
}
fieldMethodsClass.clearObjectValue();
};
//
clearObjectValue = () => {
modelObjectValue.value.splice(0, modelObjectValue.value.length);
};
}
const fieldMethodsClass = new FieldMethods();
const valueUseColumnName = props.grid['primaryKey'] || 'id';
const displayValueComputed = computed(() => {
let result = '';
if (modelObjectValue.value.length > 0) {
modelObjectValue.value.forEach((item) => {
result = result + ',' + item['displayValue'];
});
result = result.substring(1, result.length);
}
return result;
});
//
const getActualDisplayValue = (row) => {
if (!Tools.isEmpty(props.displayValue) && typeof props.displayValue === 'function') {
return props.displayValue({
data: toRaw(row),
grid: gridRef.value,
});
} else if (!Tools.isEmpty(props.displayValue) && typeof props.displayValue === 'string') {
return row[props.displayValue];
}
return '';
};
// checkbox
const updateTickeds = (args) => {
fieldMethodsClass.clearValue();
if (args.value && Array.isArray(modelValue.value)) {
const rows = gridRef.value.getRows();
rows.forEach((item) => {
modelValue.value.push(item[valueUseColumnName]);
modelObjectValue.value.push({ value: item[valueUseColumnName], displayValue: getActualDisplayValue(item) });
});
}
};
// checkbox
const updateTicked = (args) => {
if (Array.isArray(modelValue.value)) {
if (props.grid['tree']) {
fieldMethodsClass.clearValue();
const rows = gridRef.value.getTickedRows();
if (rows?.length > 0) {
rows.forEach((item) => {
modelValue.value.push(item[valueUseColumnName]);
modelObjectValue.value.push({ value: item[valueUseColumnName], displayValue: getActualDisplayValue(item) });
});
}
} else {
const index = modelValue.value.findIndex((item) => {
return item === args.row[valueUseColumnName];
});
if (args.row['ticked']) {
if (index < 0) {
modelValue.value.push(args.row[valueUseColumnName]);
modelObjectValue.value.push({ value: args.row[valueUseColumnName], displayValue: getActualDisplayValue(args.row) });
}
} else {
modelValue.value.splice(index, 1);
modelObjectValue.value.splice(index, 1);
}
}
}
};
const rowClick = (args) => {
const modelValue_ = args.row[valueUseColumnName];
if (props.multiple && Array.isArray(modelValue.value)) {
fieldMethodsClass.clearValue();
modelValue.value.push(modelValue_);
modelObjectValue.value.push({ value: modelValue_, displayValue: getActualDisplayValue(args.row) });
} else if (!props.multiple) {
fieldMethodsClass.clearValue();
modelValue.value = modelValue_;
modelObjectValue.value.push({ value: modelValue_, displayValue: getActualDisplayValue(args.row) });
} else {
console.info('error========模型值不匹配');
}
isShow.value = false;
};
//
const setObjectValueByValue = async (value) => {
if ((Array.isArray(value) && value.length > 0) || (typeof value === 'string' && !Tools.isEmpty(value))) {
const urlSearchParams = new URLSearchParams();
urlSearchParams.append(
'criteria',
JSON.stringify({
fieldName: valueUseColumnName,
operator: 'inSet',
value: Array.isArray(value) ? value : [value],
}),
);
const resp = await axios.get(props.grid['fetchDataUrl'] || props.grid['dataUrl'], { params: urlSearchParams }).catch((error) => {
console.info('error-------------', error);
});
if (resp && resp.data) {
const responseData = resp.data;
if (Array.isArray(responseData) && responseData.length > 0) {
fieldMethodsClass.clearObjectValue();
responseData.forEach((item) => {
modelObjectValue.value.push({ value: item[valueUseColumnName], displayValue: getActualDisplayValue(item) });
});
} else if (typeof responseData === 'object' && responseData.content?.length > 0) {
fieldMethodsClass.clearObjectValue();
responseData.content.forEach((item) => {
modelObjectValue.value.push({ value: item[valueUseColumnName], displayValue: getActualDisplayValue(item) });
});
}
}
}
};
onBeforeMount(() => {
//
setObjectValueByValue(modelValue.value);
});
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
getObjectValue: fieldMethodsClass.getObjectValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

153
io.sc.platform.core.frontend/src/platform/components/select/WInputSelect.vue

@ -0,0 +1,153 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="textSelectRef"
v-model="modelDisplayValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
:autogrow="true"
v-bind="attrs"
:bottom-slots="counter"
type="text"
:rules="FieldMethods.getRules(props, { value: modelValue, displayValue: modelDisplayValue }, textSelectRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, { value: modelValue, displayValue: modelDisplayValue })"
:disable="FieldMethods.getDisable(props, { value: modelValue, displayValue: modelDisplayValue })"
:clearable="false"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
<template v-if="!props['form'] || (props['form'] && props['form'].getStatus() !== 'view')" #append>
<q-btn v-if="!Tools.isEmpty(modelValue)" flat square unelevated dense icon="cancel" @click="fieldMethodsClass.clearValue"></q-btn>
<q-btn flat square unelevated dense :icon="isShow ? 'arrow_drop_up' : 'arrow_drop_down'" @click="() => (isShow = !isShow)">
<q-menu anchor="bottom start" self="top left" :offset="[50, 0]">
<q-list style="min-width: 100px">
<template v-for="(option, index) in options" :key="index">
<q-item v-close-popup clickable>
<q-item-section @click="sectionClick(option)">{{ typeof option === 'object' ? option['label'] : option }}</q-item-section>
</q-item>
</template>
</q-list>
</q-menu>
</q-btn>
</template>
<template v-if="counter" #counter>
<div>{{ modelValue?.length }}</div>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { Tools } from '@/platform';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { onBeforeMount, ref, useAttrs, watch } from 'vue';
import { FormFieldMethods } from '../form/FormField';
const textSelectRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string>();
const modelDisplayValue = ref(''); //
const isShow = ref(false);
interface FieldProps extends FormFieldProps {
counter?: boolean;
options: Array<string> | Array<object>;
}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
counter: false, //
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
displayValue: modelDisplayValue.value,
form: props['form'],
});
}
};
validate = () => {
return textSelectRef.value.validate();
};
setValue = (value) => {
modelValue.value = value;
setDiaplayValueByValue(value);
};
getValue = () => {
return modelValue.value;
};
getDisplayValue = () => {
return modelDisplayValue.value;
};
//
clearValue = () => {
modelValue.value = undefined;
modelDisplayValue.value = '';
};
}
const fieldMethodsClass = new FieldMethods();
const findOption = (value, field = 'label') => {
return props.options.find((option) => {
if (typeof option === 'object') {
return option[field] === value;
} else {
return option === value;
}
});
};
watch(
() => modelDisplayValue.value,
(newVal, oldVal) => {
const option = findOption(newVal);
if (newVal && Tools.isEmpty(option)) {
modelValue.value = newVal;
} else if (newVal && !Tools.isEmpty(option)) {
modelValue.value = typeof option === 'object' ? option['value'] : option;
} else if (!newVal) {
modelValue.value = undefined;
}
},
);
const sectionClick = (option) => {
if (typeof option === 'object') {
modelValue.value = option['value'];
modelDisplayValue.value = option['label'];
} else {
modelValue.value = option;
modelDisplayValue.value = option;
}
};
//
const setDiaplayValueByValue = async (value) => {
if (!Tools.isEmpty(value)) {
const option = findOption(value, 'value');
if (!Tools.isEmpty(option)) {
if (typeof option === 'object') {
modelDisplayValue.value = option['label'];
} else {
modelDisplayValue.value = value;
}
} else {
modelDisplayValue.value = value;
}
}
};
onBeforeMount(() => {
setDiaplayValueByValue(modelValue.value);
});
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
getDisplayValue: fieldMethodsClass.getDisplayValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

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

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

66
io.sc.platform.core.frontend/src/platform/components/select/WSelect.vue

@ -0,0 +1,66 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-select
ref="selectRef"
v-model="modelValue"
emit-value
map-options
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
:rules="FieldMethods.getRules(props, modelValue, selectRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
</q-select>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const selectRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string | number | boolean | Array<string> | Array<number> | Array<boolean>>();
interface FieldProps extends FormFieldProps {}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return selectRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

304
io.sc.platform.core.frontend/src/platform/components/select/WUserSelect.vue

@ -0,0 +1,304 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="textSelectRef"
v-model="displayValueComputed"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
:autogrow="true"
v-bind="attrs"
:bottom-slots="counter"
type="text"
:rules="FieldMethods.getRules(props, { value: modelValue, displayValue: displayValueComputed }, textSelectRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, { value: modelValue, displayValue: displayValueComputed })"
:disable="FieldMethods.getDisable(props, { value: modelValue, displayValue: displayValueComputed })"
:clearable="false"
@update:model-value="fieldMethodsClass.updateValue"
@focus="
() => {
textSelectRef?.blur();
}
"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
<template v-if="!props['form'] || (props['form'] && props['form'].getStatus() !== 'view')" #append>
<q-btn v-if="!Tools.isEmpty(displayValueComputed)" flat square unelevated dense icon="cancel" @click="fieldMethodsClass.clearValue"></q-btn>
<q-btn flat square unelevated dense icon="person_search">
<q-popup-proxy v-model:model-value="isShow" anchor="bottom right" self="top right" :offset="[0, 10]">
<q-splitter v-model="leftWidthRef" style="width: 800px; height: 300px; position: relative">
<template #before>
<w-grid
ref="orgGridRef"
:dense="true"
:tree="true"
:fetch-data-url="Environment.apiContextPath('/api/system/org')"
:config-button="false"
:checkbox-selection="false"
db-click-operation="expand"
:columns="[{ name: 'name', label: $t('org') }]"
@row-click="orgRowClick"
>
</w-grid>
</template>
<template #after>
<w-grid
ref="userGridRef"
:checkbox-selection="props.multiple || false"
:dense="true"
:fetch-data-url="userGridFetchDataUrl"
:config-button="false"
title="用户列表"
:sort-by="['loginName']"
:query-form-cols-num="2"
:query-form-fields="[
{ name: 'loginName', label: $t('loginName'), type: 'w-text' },
{ name: 'userName', label: $t('userName'), type: 'w-text' },
]"
:toolbar-actions="['query', 'separator', 'reset']"
:ticked-record="{
columnName: valueUseColumnName,
data: typeof modelValue === 'string' ? [modelValue] : modelValue,
}"
:columns="[
{ name: 'loginName', label: $t('loginName') },
{ name: 'userName', label: $t('userName') },
{ name: 'description', label: $t('description') },
{ name: 'enable', label: $t('status'), format: Formater.enableTag() },
]"
@row-click="userRowClick"
@update-ticked="updateTicked"
@update-tickeds="updateTickeds"
>
</w-grid>
</template>
</q-splitter>
</q-popup-proxy>
</q-btn>
</template>
<template v-if="counter" #counter>
<div>{{ modelValue?.length }}</div>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, onBeforeMount, toRaw } from 'vue';
import { Tools, axios, Environment, Formater } from '@/platform';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const textSelectRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string | Array<string>>();
const modelObjectValue = ref(<any>[]); //
const orgGridRef = ref();
const userGridRef = ref();
const userGridFetchDataUrl = Environment.apiContextPath('/api/system/user');
const userGridFetchDataByOrgUrl = Environment.apiContextPath('/api/system/user/queryUsersByOrgAndQueryParameter');
const leftWidthRef = ref(30);
const isShow = ref(false);
interface FieldProps extends FormFieldProps {
multiple?: boolean;
counter?: boolean;
valueUseId?: boolean;
displayValue?: string | ((args: object) => '');
}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
multiple: false, //
counter: false, //
/**
* 值使用用户id默认绑定的实际值为用户登录账号true则使用id绑定
*/
valueUseId: false,
/**
* 显示值配置可使用选项
* account用户登陆账号
* name用户名
* nameAppendAccount用户名追加登陆账号示例张三(zhangsan)组件默认使用该选项
* accountAppendName账号追加用户名示例zhangsan(张三)
* 自定义函数自己定义显示的内容组件会将用户数据传到函数中函数必须返回一个字符串示例
* displayValue: (args) => {
* return args['data']['loginName'] + '_' + args['data']['userName'];
* }
*/
displayValue: 'nameAppendAccount',
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
displayValue: displayValueComputed.value,
form: props['form'],
});
}
};
validate = () => {
return textSelectRef.value.validate();
};
setValue = (value) => {
if (props.multiple && Array.isArray(value) && Array.isArray(modelValue.value)) {
fieldMethodsClass.clearValue();
modelValue.value.push(...value);
setObjectValueByValue(value);
} else if (!props.multiple && !Array.isArray(value)) {
modelValue.value = value;
setObjectValueByValue(value);
} else {
console.info('error========模型值不匹配');
}
};
getValue = () => {
return modelValue.value;
};
getObjectValue = () => {
return modelObjectValue.value;
};
//
clearValue = () => {
if (props.multiple && Array.isArray(modelValue.value)) {
modelValue.value.splice(0, modelValue.value.length);
} else {
modelValue.value = undefined;
}
fieldMethodsClass.clearObjectValue();
};
//
clearObjectValue = () => {
modelObjectValue.value.splice(0, modelObjectValue.value.length);
};
}
const fieldMethodsClass = new FieldMethods();
const valueUseColumnName = props.valueUseId ? 'id' : 'loginName';
const displayValueComputed = computed(() => {
let result = '';
if (modelObjectValue.value.length > 0) {
modelObjectValue.value.forEach((item) => {
result = result + ',' + item['displayValue'];
});
result = result.substring(1, result.length);
}
return result;
});
const getActualDisplayValue = (row) => {
if (!Tools.isEmpty(props.displayValue) && typeof props.displayValue === 'function') {
return props.displayValue({
data: toRaw(row),
orgGrid: orgGridRef.value,
userGrid: userGridRef.value,
});
} else if (!Tools.isEmpty(props.displayValue) && typeof props.displayValue === 'string') {
if (props.displayValue === 'account') {
return row['loginName'];
} else if (props.displayValue === 'name') {
return row['userName'];
} else if (props.displayValue === 'accountAppendName') {
return row['loginName'] + '(' + row['userName'] + ')';
} else if (props.displayValue === 'nameAppendAccount') {
return row['userName'] + '(' + row['loginName'] + ')';
}
}
return '';
};
const updateTickeds = (args) => {
if (args.value && Array.isArray(modelValue.value)) {
fieldMethodsClass.clearValue();
const rows = userGridRef.value.getRows();
rows.forEach((item) => {
modelValue.value.push(item[valueUseColumnName]);
modelObjectValue.value.push({ value: item[valueUseColumnName], displayValue: getActualDisplayValue(item) });
});
} else {
fieldMethodsClass.clearValue();
}
};
const updateTicked = (args) => {
if (Array.isArray(modelValue.value)) {
const index = modelValue.value.findIndex((item) => {
return item === args.row[valueUseColumnName];
});
if (args.row['ticked']) {
if (index < 0) {
modelValue.value.push(args.row[valueUseColumnName]);
modelObjectValue.value.push({ value: args.row[valueUseColumnName], displayValue: getActualDisplayValue(args.row) });
}
} else {
modelValue.value.splice(index, 1);
modelObjectValue.value.splice(index, 1);
}
}
};
const userRowClick = (args) => {
const modelValue_ = args.row[valueUseColumnName];
if (props.multiple && Array.isArray(modelValue.value)) {
fieldMethodsClass.clearValue();
modelValue.value.push(modelValue_);
modelObjectValue.value.push({ value: modelValue_, displayValue: getActualDisplayValue(args.row) });
} else if (!props.multiple) {
fieldMethodsClass.clearValue();
modelValue.value = modelValue_;
modelObjectValue.value.push({ value: modelValue_, displayValue: getActualDisplayValue(args.row) });
} else {
console.info('error========模型值不匹配');
}
isShow.value = false;
};
const orgRowClick = (args) => {
userGridRef.value.setFetchDataUrl(userGridFetchDataByOrgUrl + '?orgId=' + args.row['id']);
userGridRef.value.refresh();
};
//
const setObjectValueByValue = async (value) => {
if ((Array.isArray(value) && value.length > 0) || (typeof value === 'string' && !Tools.isEmpty(value))) {
const urlSearchParams = new URLSearchParams();
urlSearchParams.append(
'criteria',
JSON.stringify({
fieldName: valueUseColumnName,
operator: 'inSet',
value: Array.isArray(value) ? value : [value],
}),
);
const resp = await axios.get(userGridFetchDataUrl, { params: urlSearchParams }).catch((error) => {
console.info('error-------------', error);
});
if (resp && resp.data) {
const responseData = resp.data;
if (Array.isArray(responseData) && responseData.length > 0) {
fieldMethodsClass.clearObjectValue();
responseData.forEach((item) => {
modelObjectValue.value.push({ value: item[valueUseColumnName], displayValue: getActualDisplayValue(item) });
});
} else if (typeof responseData === 'object' && responseData.content?.length > 0) {
fieldMethodsClass.clearObjectValue();
responseData.content.forEach((item) => {
modelObjectValue.value.push({ value: item[valueUseColumnName], displayValue: getActualDisplayValue(item) });
});
}
}
}
};
onBeforeMount(() => {
setObjectValueByValue(modelValue.value);
});
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
getObjectValue: fieldMethodsClass.getObjectValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

85
io.sc.platform.core.frontend/src/platform/components/text-editor/WTextEditor.vue

@ -0,0 +1,85 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-editor
ref="textEditorRef"
v-model="modelValue"
:dense="true"
:toolbar="[
['label'],
['removeFormat', 'viewsource'],
[
{
label: $q.lang.editor.align,
icon: $q.iconSet.editor.align,
fixedLabel: true,
options: ['left', 'center', 'right', 'justify'],
},
],
['bold', 'italic', 'strike', 'underline', 'subscript', 'superscript', 'hr'],
[
{
label: $q.lang.editor.fontSize,
icon: $q.iconSet.editor.fontSize,
fixedLabel: true,
fixedIcon: true,
list: 'no-icons',
options: ['size-1', 'size-2', 'size-3', 'size-4', 'size-5', 'size-6', 'size-7'],
},
],
['print', 'fullscreen'],
]"
v-bind="attrs"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label>
<span>{{ attrs.label }}</span>
</template>
</q-editor>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const textRef = ref();
const attrs = useAttrs();
const modelValue = defineModel({ type: String, default: '' });
interface FieldProps extends FormFieldProps {}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return textRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = '';
};
}
const fieldMethodsClass = new FieldMethods();
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

80
io.sc.platform.core.frontend/src/platform/components/text/WText.vue

@ -0,0 +1,80 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="textRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="text"
:rules="FieldMethods.getRules(props, modelValue, textRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs, computed } from 'vue';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const textRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string>();
interface FieldProps extends FormFieldProps {}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return textRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
// const showComputed = computed(() => {
// return FieldMethods.getShow(props, modelValue.value);
// });
// const rulesComputed = computed(() => {
// return FieldMethods.getRules(props, modelValue.value, textRef, undefined);
// });
// const readOnlyComputed = computed(() => {
// return FieldMethods.getReadOnly(props, modelValue.value);
// });
// const disableComputed = computed(() => {
// return FieldMethods.getDisable(props, modelValue.value);
// });
// const requiredComputed = computed(() => {
// return FieldMethods.getRequired(props, modelValue.value);
// });
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

65
io.sc.platform.core.frontend/src/platform/components/textarea/WTextarea.vue

@ -0,0 +1,65 @@
<template>
<div v-show="FieldMethods.getShow(props, modelValue)">
<q-input
ref="textareaRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="textarea"
:rules="FieldMethods.getRules(props, modelValue, textareaRef, undefined)"
:readonly="FieldMethods.getReadOnly(props, modelValue)"
:disable="FieldMethods.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="FieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const textareaRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string>();
interface FieldProps extends FormFieldProps {}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
form: props['form'],
});
}
};
validate = () => {
return textareaRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

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

@ -135,12 +135,13 @@ export {
WNumber,
WTextarea,
WSelect,
WTreeSelect,
WGridSelect,
WOrgSelect,
WUserSelect,
WLabel,
WDate,
WCheckbox,
WTextBtn,
WPassword,
WOptionGroup,
WGrid,
WIconEmpty,
WVExpandDiv,

14
io.sc.platform.core.frontend/src/routes/routes.json

@ -38,20 +38,6 @@
"permissions": ["/testcase/math/**/*"]
}
},
{
"name": "route.testcase.mathEditorForm",
"path": "testcase/mathEditorForm",
"parent": "/",
"priority": 0,
"component": "component.testcase.mathEditorForm",
"componentPath": "@/views/testcase/math/MathEditorForm.vue",
"redirect": null,
"meta": {
"permissions": ["/testcase/math/**/*"]
}
},
{
"name": "route.testcase.form",
"path": "testcase/form",

88
io.sc.platform.core.frontend/src/views/Editor.vue

@ -2,52 +2,52 @@
<div ref="containerRef"></div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { type CellStyle, Graph, InternalEvent } from '@maxgraph/core';
// import { ref, onMounted } from 'vue';
// import { type CellStyle, Graph, InternalEvent } from '@maxgraph/core';
const containerRef = ref();
// const containerRef = ref();
onMounted(() => {
//const container = <HTMLElement>document.getElementById('graph-container');
// Disables the built-in context menu
InternalEvent.disableContextMenu(containerRef.value);
// onMounted(() => {
// //const container = <HTMLElement>document.getElementById('graph-container');
// // Disables the built-in context menu
// InternalEvent.disableContextMenu(containerRef.value);
const graph = new Graph(containerRef.value);
graph.setPanning(true); // Use mouse right button for panning
// Gets the default parent for inserting new cells. This
// is normally the first child of the root (ie. layer 0).
const parent = graph.getDefaultParent();
// const graph = new Graph(containerRef.value);
// graph.setPanning(true); // Use mouse right button for panning
// // Gets the default parent for inserting new cells. This
// // is normally the first child of the root (ie. layer 0).
// const parent = graph.getDefaultParent();
// Adds cells to the model in a single step
graph.batchUpdate(() => {
const vertex01 = graph.insertVertex({
parent,
position: [10, 10],
size: [100, 100],
value: 'rectangle',
});
const vertex02 = graph.insertVertex({
parent,
position: [350, 90],
size: [50, 50],
style: {
fillColor: 'orange',
shape: 'ellipse',
verticalAlign: 'top',
verticalLabelPosition: 'bottom',
},
value: 'ellipse',
});
graph.insertEdge({
parent,
source: vertex01,
target: vertex02,
value: 'edge',
style: {
edgeStyle: 'orthogonalEdgeStyle',
rounded: true,
},
});
});
});
// // Adds cells to the model in a single step
// graph.batchUpdate(() => {
// const vertex01 = graph.insertVertex({
// parent,
// position: [10, 10],
// size: [100, 100],
// value: 'rectangle',
// });
// const vertex02 = graph.insertVertex({
// parent,
// position: [350, 90],
// size: [50, 50],
// style: {
// fillColor: 'orange',
// shape: 'ellipse',
// verticalAlign: 'top',
// verticalLabelPosition: 'bottom',
// },
// value: 'ellipse',
// });
// graph.insertEdge({
// parent,
// source: vertex01,
// target: vertex02,
// value: 'edge',
// style: {
// edgeStyle: 'orthogonalEdgeStyle',
// rounded: true,
// },
// });
// });
// });
</script>

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

@ -1,169 +1,232 @@
<template>
<div>
<w-form ref="formRef" :fields="aaaa.fields" :cols-x-gap="8"> </w-form>
<q-btn label="提交" @click="submit"></q-btn>&nbsp; <q-btn label="重置" @click="reset"></q-btn>&nbsp;
<q-btn label="初始化值" @click="setValue"></q-btn>&nbsp;
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { EnumTools, Options } from '@/platform';
import { FormValidators } from '@/platform/components';
import { useI18n } from 'vue-i18n';
const UrlOpenTypeEnum = await EnumTools.fetch('io.sc.platform.system.enums.UrlOpenType');
const { t } = useI18n();
const formRef = ref();
const aaaa = {
fields: [
<w-form
ref="formRef"
:fields="[
{
label: t('theme.main'),
name: 'name',
type: 'password',
// maxlength: 10,
requiredIf: (form) => {
console.info('form====', form.getData().age);
return false;
},
rules: [
() => {
return true;
name: 'test1',
label: '文本框',
type: 'w-text',
requiredIf: true,
},
{
name: 'test2',
label: '下拉框',
type: 'w-select',
requiredIf: true,
multiple: true,
options: [
{ label: '男', value: 1 },
{ label: '女', value: 0 },
],
colspan: 2,
// requiredIf: () => false,
// 'onUpdate:modelValue': (value) => {
// formRef.value.data.ah = '111';
// },
onFocus: () => {
// console.info('33333333');
},
},
// {
// label: '',
// name: 'org',
// type: 'w-text-btn',
// requiredIf: () => true,
// // buttonPosition: 'prepend',
// button: {
// icon: 'home',
// click: () => {
// console.info('');
// },
// },
// },
{
name: 'urlOpenType',
label: 'urlOpenType',
type: 'select',
options: Options.enum(UrlOpenTypeEnum, false),
defaultValue: 'NEW_WINDOW',
},
{
label: '年龄',
name: 'age',
type: 'w-number',
defaultValue: 111,
precision: 0,
requiredIf: () => true,
onUpdateValue: (args) => {
console.info('value========', args);
},
},
{
label: '出生日期',
name: 'cs',
type: 'w-date',
requiredIf: () => true,
name: 'test2.5',
label: '可输入可选择下拉框',
type: 'w-input-select',
requiredIf: true,
defaultValue: 'wowowo',
options: [
{ label: 'java', value: '1' },
{ label: 'python', value: '2' },
],
},
{
label: '爱好',
name: 'ah',
type: 'w-text',
rules: [],
reactiveRules: true,
requiredIf: () => true,
},
{
label: '性别',
name: 'sex',
type: 'select',
defaultValue: '0',
options: ['1', '0'],
requiredIf: () => true,
'onUpdate:modelValue': (value) => {
// if (value === 1) {
// formRef.value.fields.addr.label = '2222';
// } else {
// formRef.value.fields.addr.label = '3333';
// }
},
},
{
label: '地址',
name: 'addr',
rows: 2,
colspan: 'full',
type: 'w-textarea',
// hideIf: () => {
// if (formRef.value) {
// if (formRef.value.data.sex && formRef.value.data.sex === 1) {
// return true;
// } else {
// return false;
// }
// }
// return false;
// },
requiredIf: () => true,
// readonlyIf: () => {
// return true;
// },
disableIf: () => {
return false;
},
},
{
label: '是否可用1',
name: 'ky1',
colsFirst: true,
colspan: 2,
type: 'w-checkbox',
// showIf: (form) => {
// if (form.getFieldValue('sex') === '1') {
// return true;
// } else {
// return false;
// }
// },
// disableIf: () => {
// return true;
// },
},
{
label: '是否可用2',
name: 'ky2',
name: 'test3',
label: '复选框',
type: 'w-checkbox',
onUpdateValue: (args) => {
console.info('value========', args);
},
},
{
label: '是否可用3',
name: 'ky3',
type: 'w-checkbox',
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,
requiredIf: (args) => {
return true;
},
onUpdateValue: (args) => {
// console.info('value========', args);
},
},
{
name: 'test7',
label: '密码',
type: 'w-password',
requiredIf: true,
},
{
name: 'test8',
label: '头像',
type: 'w-icon',
requiredIf: true,
},
{
name: 'test9',
label: '页面位置',
type: 'w-position',
requiredIf: true,
},
{
name: 'test10',
label: '颜色',
type: 'w-color-input',
requiredIf: true,
},
{
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' },
],
};
},
{
name: 'test12.5',
label: '单选按钮',
type: 'w-radio',
requiredIf: true,
options: [
{ label: '已婚', value: 1 },
{ label: '单身', value: 2 },
{ label: '离异', value: 3 },
],
},
{
name: 'test13',
label: '自定义下拉表格',
type: 'w-grid-select',
requiredIf: true,
multiple: true,
displayValue: 'name',
grid: {
title: '应用列表',
dataUrl: Environment.apiContextPath('/api/system/application'),
sortBy: ['order'],
queryFormColsNum: 3,
queryFormFields: [
{ name: 'code', label: $t('code') },
{ name: 'name', label: $t('name') },
{ name: 'enable', label: $t('isEnable') },
],
toolbarActions: ['query', 'separator', 'reset'],
columns: [
{ name: 'order', label: $t('order') },
{ name: 'code', label: $t('code') },
{
name: 'name',
label: $t('name'),
format: (val, row) => {
return val;
},
},
{ name: 'enable', label: $t('status'), format: Formater.enableTag() },
{ name: 'lastModifier', label: $t('lastModifier') },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
],
},
},
{
name: 'test14',
label: '用户选择',
type: 'w-user-select',
multiple: true,
requiredIf: true,
},
{
name: 'test15',
label: '机构选择',
type: 'w-org-select',
requiredIf: true,
},
{
name: 'test16',
label: 'cron表达式',
type: 'w-cron',
requiredIf: true,
},
{
name: 'test98',
label: '代码编辑器',
type: 'w-code-mirror',
requiredIf: true,
colsFirst: true,
colSpan: '2',
rows: 3,
},
{
name: 'test99',
label: '文本域',
type: 'w-textarea',
requiredIf: true,
colSpan: '2',
rows: 7,
},
{
name: 'test100',
label: '富文本编辑器',
type: 'w-text-editor',
required: true,
colsFirst: true,
colSpan: 'full',
},
]"
:cols-x-gap="8"
>
</w-form>
<q-btn label="提交" @click="submit"></q-btn>&nbsp; <q-btn label="重置" @click="reset"></q-btn>&nbsp;
<q-btn label="初始化值" @click="setValue"></q-btn>&nbsp;
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { EnumTools, Options, Environment, Formater } from '@/platform';
import { FormValidators } from '@/platform/components';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const formRef = ref();
const submit = async () => {
// aaaa.fields[1].required = false;
// const validateResult = await formRef.value.validate();
// console.info('', validateResult);
const errors = [
{ fieldName: 'name', errorMessage: '个数必须在1和4之间' },
{ fieldName: 'name', errorMessage: '不能包含@符号' },
{ fieldName: 'age', errorMessage: '年龄不能为负数' },
];
formRef.value.setValidationErrors(errors);
// console.info('', formRef.value.getData());
// formRef.value.fieldsMap.get('name').required = false;
// console.info('xxx===', formRef.value.fieldsMap.get('ah'));
// const validateResult = await formRef.value.validate();
console.info('表单数据:', formRef.value.getData());
const validateResult = await formRef.value.validate();
console.info('表单验证结果:', validateResult);
};
const reset = () => {
formRef.value.reset();

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

@ -1,570 +1,31 @@
<template>
<div style="height: 100%">
<!-- <q-splitter v-model="splitterModel">
<template #before>
<q-tabs v-model="tab" vertical class="text-teal">
<q-tab name="mails" icon="mail" label="Mails" />
<q-tab name="alarms" icon="alarm" label="Alarms" />
<q-tab name="movies" icon="movie" label="Movies" />
</q-tabs>
</template>
<template #after>
<q-tab-panels v-model="tab" animated swipeable vertical transition-prev="jump-up" transition-next="jump-up">
<q-tab-panel name="mails">
</q-tab-panel>
<q-tab-panel name="alarms">
<div class="h-full">
<w-grid
ref="grid2"
:title="testGrid.title"
draggable
dense
:data-url="testGrid.tableDataUrl"
:checkbox-selection="true"
selection="multiple"
:query-form-cols-num="2"
:columns="testGrid.tableColumns"
:toolbar-actions="testGrid.toolbar"
:query-form-fields="testGrid.queryForm"
:editor="{
form: {
colsNum: 1,
fields: [
{ label: '登录名', name: 'loginName', type: 'w-text' },
{ label: '用户名', name: 'userName', type: 'w-text' },
{ label: '密码', name: 'password', type: 'w-text' },
{ label: '是否可用1111', name: 'enable', type: 'w-checkbox' },
{
name: 'urlOpenType',
label: 'urlOpenType',
type: 'select',
options: Options.enum(UrlOpenTypeEnum, false),
defaultValue: 'NEW_WINDOW',
},
],
},
}"
:viewer="testGrid.view"
></w-grid>
</q-tab-panel>
<q-tab-panel name="movies">
<div class="text-h4 q-mb-md">Movies</div>
<w-form ref="form" :fields="[{ name: 'a', label: 'a', type: 'text' }]"></w-form>
</q-tab-panel>
</q-tab-panels>
</template>
</q-splitter> -->
<!-- <w-grid
ref="gridRef"
:title="testGrid.title"
draggable
:dense="state.dense"
:hide-bottom="false"
:data-url="testGrid.tableDataUrl"
:auto-fetch-data="true"
:checkbox-selection="true"
selection="multiple"
:query-form-cols-num="6"
:columns="testGrid.tableColumns"
:toolbar-actions="testGrid.toolbar"
:query-form-fields="testGrid.queryForm"
:editor="{
form: {
colsNum: 1,
fields: [
{ label: '登录名', name: 'loginName', type: 'w-text' },
{ label: '用户名', name: 'userName', type: 'w-text' },
{ label: '密码', name: 'password', type: 'w-text' },
{ label: '是否可用1111', name: 'enable', type: 'w-checkbox' },
{
name: 'urlOpenType',
label: 'urlOpenType',
type: 'select',
options: Options.enum(UrlOpenTypeEnum, false),
defaultValue: 'NEW_WINDOW',
},
],
},
}"
:viewer="testGrid.view"
@update-ticked="updateTicked"
@row-click="rowClick"
@row-db-click="rowDbClick"
@after-request-data="
() => {
}
"
></w-grid> -->
<w-grid
ref="applicationGridRef"
:title="$t('system.application.grid.title')"
:config-button="true"
selection="multiple"
:checkbox-selection="true"
:sort-no="true"
:draggable="true"
title="示例列表"
:data-url="Environment.apiContextPath('/api/system/application')"
:sort-by="['order']"
:query-form-cols-num="3"
db-click-operation="rowEdit"
:query-form-fields="[
{ name: 'code', label: $t('code'), type: 'text' },
{ name: 'name', label: $t('name'), type: 'text' },
{ name: 'enable', label: $t('isEnable'), type: 'select', options: Options.yesNo() },
]"
:toolbar-configure="{ noIcon: false }"
:toolbar-actions="[
'query',
'refresh',
'separator',
'rowEdit',
'rowsEdit',
'separator',
'add',
'clone',
'edit',
'remove',
'separator',
'view',
'separator',
'export',
]"
:toolbar-actions="['add', 'edit']"
:columns="[
{ name: 'order', label: $t('order'), type: 'number', attrs: { required: true } },
{ name: 'code', label: $t('code'), type: 'text', attrs: { required: true } },
{ name: 'code', label: '编码', type: 'w-text' },
{ name: 'name', label: '名称', type: 'w-text' },
{
name: 'name',
label: $t('name'),
type: 'text',
format: (val, row) => {
return val;
},
name: 'enable',
label: '状态',
format: Formater.enableTag(),
},
{ width: 80, name: 'enable', label: $t('status'), format: Formater.enableTag(), type: 'checkbox' },
{ width: 120, name: 'lastModifier', label: $t('lastModifier') },
{ width: 120, name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.dateOnly() },
{ name: 'lastModifier', label: '最后修改人', align: 'center' },
{ name: 'lastModifyDate', label: '最后修改日期', align: 'center' },
]"
:editor="{
dialog: {
width: '600px',
height: '300px',
},
form: {
colsNum: 1,
fields: [
{ name: 'code', label: $t('code'), type: 'text', required: true },
{ name: 'name', label: $t('name'), type: 'text', required: true },
{ name: 'description', label: $t('description'), type: 'textarea', rows: 1 },
{ name: 'order', label: $t('order'), type: 'number' },
{ name: 'enable', label: $t('enable'), type: '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: 'order', label: $t('order') },
{ 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="
(evt, row, index) => {
// console.info('rowclick');
}
"
@before-request-data="() => {}"
@after-row-draggable="
(grid, updateDatas) => {
// console.info('grid=====', grid);
// console.info('updateDatas======', updateDatas);
}
"
@after-editor-open="
() => {
console.info('打开窗口');
}
"
@row-click="rowClick"
>
</w-grid>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick, reactive, computed } from 'vue';
import { axios, Environment, EnumTools, Options, Formater } from '@/platform';
import EnableIcon from '@/platform/components/grid/EnableIcon.vue';
import { IconEnum } from '@/platform/enums';
// :query-criteria="{
// operator: 'equals',
// fieldName: 'code',
// value: 'A',
// }"
import { ref } from 'vue';
import { Environment, Formater } from '@/platform';
const dialogRef = ref();
const gridRef = ref();
const grid2 = ref();
const form = ref();
const tab = ref('mails');
const splitterModel = ref(10);
const UrlOpenTypeEnum = await EnumTools.fetch('io.sc.platform.system.enums.UrlOpenType');
const updateTicked = (event, row) => {
console.info('grid.updateTicked.event====', event);
console.info('grid.updateTicked.row====', row);
};
const state = reactive({
dense: false,
});
const startY = ref(0);
const endY = ref(0);
const rowClick = (evt, row, index) => {
// if (startY.value === 0) {
// startY.value = evt.clientY;
// } else if (evt.shiftKey && endY.value === 0) {
// endY.value = evt.clientY;
// }
// console.info('startY====', startY.value);
// console.info('endY====', endY.value);
// // startY endY
// if (startY.value > 0 && endY.value > 0) {
// console.info(' startY endY');
// startY.value = 0;
// endY.value = 0;
// }
// startY.value = 0;
// endY.value = 0;
// console.info('grid.rowClick.row====', row);
// tab.value = 'alarms';
// nextTick(() => {
// grid2.value.getQueryForm().setFieldValue('loginName', row.id);
// });
// tab.value = 'movies';
// nextTick(() => {
// form.value.setFieldValue('a', row.id);
// });
console.info('grid.rowClick.row====', row);
};
const rowDbClick = (evt, row, index) => {
console.info('grid.rowDbClick.row====', row);
};
//
const getElementViewPosition = (element) => {
//x
let actualLeft = element.offsetLeft;
let xcurrent = element.offsetParent;
while (xcurrent !== null) {
actualLeft += xcurrent.offsetLeft + xcurrent.clientLeft;
xcurrent = xcurrent.offsetParent;
}
let elementScrollLeft = document.documentElement.scrollLeft;
if (document.compatMode == 'BackCompat') {
elementScrollLeft = document.body.scrollLeft;
}
const left = actualLeft - elementScrollLeft;
//y
let actualTop = element.offsetTop;
let ycurrent = element.offsetParent;
while (ycurrent !== null) {
actualTop += ycurrent.offsetTop + ycurrent.clientTop;
ycurrent = ycurrent.offsetParent;
}
let elementScrollTop = document.documentElement.scrollTop;
if (document.compatMode == 'BackCompat') {
elementScrollTop = document.body.scrollTop;
}
var right = actualTop - elementScrollTop;
//
return { x: left, y: right };
};
// const gridHeightComputed = computed(() => {
// // if (dialogRef?.value && gridRef?.value) {
// // const height = dialogRef.value.getContentHeight();
// // const tableContentY = getElementViewPosition(gridRef.value.$el.getElementsByClassName('q-table__middle')[0]);
// // console.info('height==========', height);
// // console.info('gridRef==============', tableContentY);
// // if (height && tableContentY) {
// // return height.height - (tableContentY.y - height.y);
// // }
// // }
// if (dialogRef?.value && dialogRef.value.getContent()) {
// const height = dialogRef.value.getContent().clientHeight;
// console.info('dialogRef.value.getContent()====', height);
// if (height > 0) {
// return height;
// }
// }
// return 0;
// });
const aaaaaaaaaaaaaaaa = ref(false);
const testGrid = {
hideBottom: false,
autoLoadData: false,
tableLeftColumnStickyNumber: 1,
title: '用户列表',
tableDataUrl: Environment.apiContextPath('api/system/user'),
queryCriteria: {
fieldName: 'loginName',
operator: 'contains',
value: '1',
},
toolbar: [
['query', 'separator', 'moreQuery'],
'reset',
{
extend: 'refresh',
click: () => {
state.dense = !state.dense;
},
},
'separator',
{
extend: 'resetDefaultValues',
icon: undefined,
label: 'aasdfFFF',
loadingIf: (selected, ticked, grid) => {
if (selected.length === 0) {
return true;
}
return false;
},
// enableIf: (selected) => {
// if (selected && selected.length > 0) {
// return true;
// }
// return false;
// },
// beforeClick: (selected, context, grid) => {
// console.info('before');
// context.aaa = '111';
// },
click: (args) => {
// grid.setQueryCriteria({
// fieldName: 'loginName',
// operator: 'contains',
// value: 'admin',
// });
console.info('args=====', args);
args._click();
},
afterClick: (selected, context, grid) => {
// grid.addEditFormRef.setFieldValue('userName', '');
},
},
[
{
name: 'op',
icon: 'difference',
label: '操作',
},
'add',
'edit',
'clone',
'remove',
'separator',
'view',
'export',
],
'separator',
],
tableShowSortNo: true,
queryForm: [
{ label: '登录名', name: 'loginName', type: 'w-text' },
{ label: '用户名', name: 'userName', type: 'w-text' },
{ label: '描述', name: 'description', type: 'w-text' },
{ label: '用户名', name: 'userName1', type: 'w-text' },
{ label: '描述', name: 'description1', type: 'w-text' },
{
label: '是否可用',
name: 'enable',
type: 'select',
options: [{ label: '监管报表-G4B-1表内信用风险加权资产计算表(权重法)', value: '0a981b42-d0df-4f02-94c4-edc20620ba9f' }],
},
// { label: '', name: 'email', type: 'w-text' },
// { label: '', name: 'phone', type: 'w-text' },
// { label: '', name: 'mobile', type: 'w-number' },
// { label: '', name: 'lastModifier', type: 'w-text' },
// { label: '', name: 'lastModifyDate', type: 'w-date' },
],
tableColumns: [
// {
// name: 'info',
// label: '',
// columns: [
// { name: 'loginName', label: '', align: 'right' },
// { name: 'userName', label: '' },
// ],
// },
{ name: 'loginName', label: '登录名', align: 'right' },
{ name: 'userName', label: '用户名' },
// {
// name: 'lxxx',
// label: '',
// columns: [
// {
// name: 'email',
// label: '',
// columns: [
// { width: 100, name: 'auc', label: 'auc' },
// { width: 100, name: 'ar', label: 'ar' },
// { width: 100, name: 'ks', label: 'ks' },
// ],
// },
// {
// name: 'tx',
// label: '',
// columns: [
// { name: 'phone', label: '' },
// { name: 'mobile', label: '' },
// ],
// },
// {
// name: 'qq',
// label: 'QQ',
// // columns: [
// // { width: 100, name: 'aucQualitative', label: 'aucQualitative' },
// // { width: 100, name: 'arQualitative', label: 'arQualitative' },
// // { width: 100, name: 'ksQualitative', label: 'ksQualitative' },
// // ],
// },
// ],
// },
{ name: 'email', label: '邮箱地址' },
{ name: 'phone', label: '电话' },
{ name: 'mobile', label: '手机号' },
{ name: 'qq', label: 'QQ' },
{ name: 'description', label: '描述', width: 400 },
{
name: 'enable',
label: '是否可用',
align: 'center',
width: 400,
format: (val, row) => {
return {
componentType: 'q-checkbox',
bindModelValue: true,
attrs: {
dense: true,
},
};
// return {
// componentType: 'q-icon',
// attrs: {
// name: val ? IconEnum. : IconEnum.,
// color: val ? 'green' : 'red',
// size: 'xs',
// },
// };
},
},
// { name: 'loginName', label: '', align: 'right' },
// { name: 'userName', label: '' },
{ name: 'lastModifier', label: '最后修改人' },
{ name: 'lastModifyDate', label: '最后修改时间' },
],
addEdit: {
dialog: {},
form: {
colsNum: 1,
fields: [
{ label: '登录名', name: 'loginName', type: 'w-text' },
{ label: '用户名', name: 'userName', type: 'w-text' },
{ label: '密码', name: 'password', type: 'w-text' },
{ label: '是否可用1111', name: 'enable', type: 'w-checkbox' },
{
name: 'urlOpenType',
label: 'urlOpenType',
type: 'select',
options: Options.enum(UrlOpenTypeEnum, false),
defaultValue: 'NEW_WINDOW',
},
],
},
},
view: {
panel: {
columnNum: 2,
fields: [
{ name: 'id', label: '主键' },
{ name: 'loginName', label: '登录名' },
{ name: 'userName', label: '用户名' },
{ name: 'description', label: '描述' },
{
name: 'enable',
label: '是否可用',
},
{ name: 'email', label: '邮箱地址' },
{ name: 'phone', label: '电话' },
{ name: 'mobile', label: '手机号' },
{ name: 'lastModifier', label: '最后修改人' },
{ name: 'lastModifyDate', label: '最后修改时间' },
],
},
},
};
onMounted(() => {
// gridRef.value.setLocalData([
// {
// loginName: 'admin1',
// },
// {
// loginName: 'admin2',
// },
// {
// loginName: 'admin3',
// },
// {
// loginName: 'admin4',
// },
// {
// loginName: 'admin5',
// },
// {
// loginName: 'admin6',
// },
// {
// loginName: 'admin7',
// },
// {
// loginName: 'admin8',
// },
// {
// loginName: 'admin9',
// },
// {
// loginName: 'admin10',
// },
// {
// loginName: 'admin11',
// },
// {
// loginName: 'admin12',
// },
// {
// loginName: 'admin13',
// },
// ]);
});
const rowClick = () => {};
</script>

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

@ -2,26 +2,41 @@
<div class="h-full">
<w-grid
ref="gridRef"
:title="testGrid.title"
draggable
sort-no
tree
:tree-icon="(row) => {}"
:checkbox-selection="true"
db-click-operation="rowEdit"
:data-url="testGrid.dataUrl"
:fetch-data-url="testGrid.fetchDataUrl"
:columns="testGrid.tableColumns"
:toolbar-actions="testGrid.toolbar"
:query-form-fields="testGrid.queryFormFields"
:query-form-cols-num="3"
@update-ticked="updateTicked"
@after-row-draggable="
(grid, updateDatas) => {
console.info('grid=====', grid);
console.info('updateDatas======', updateDatas);
}
"
title="树形表格示例"
:fetch-data-url="Environment.apiContextPath('api/system/menu/allMenus')"
:tree="true"
db-click-operation="expand"
draggable="local"
:columns="[
{
name: 'name',
label: '菜单名称',
type: 'text',
format: (val, row) => {
return t(row.name);
},
},
{
name: 'icon',
label: '图标',
type: 'icon',
attrs: {
required: true,
},
format: (val, row) => {
return {
componentType: 'q-icon',
attrs: {
name: val,
size: 'xs',
},
};
},
},
{ name: 'type', label: '菜单类型' },
{ name: 'order', label: '排序号' },
]"
:toolbar-actions="['expand']"
></w-grid>
</div>
</template>
@ -82,11 +97,11 @@ const testGrid = {
],
'separator',
],
queryFormFields: [
{ label: '菜单名称', name: 'name', type: 'w-password' },
{ label: '菜单类型', name: 'userName', type: 'select' },
{ label: '是否可用', name: 'enable', type: 'select' },
],
// queryFormFields: [
// { label: '', name: 'name', type: 'w-password' },
// { label: '', name: 'userName', type: 'select' },
// { label: '', name: 'enable', type: 'select' },
// ],
tableColumns: [
{
name: 'name',

232
io.sc.platform.core.frontend/template-project/src/mock/api/sample/params.json

@ -0,0 +1,232 @@
{
"enable": true,
"url": "/api/rwa/params/isc/fetchParams?pageable=false&sortBy=sortNo",
"method": "get",
"response": {
"code" : 200,
"messageI18nKey" : "success",
"message" : "success",
"data" : [ {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2023-09-05 02:02:14",
"lastModifier" : "admin",
"lastModifyDate" : "2023-09-05 02:02:14",
"corporationCode" : null,
"id" : "2268c3e2-c03f-4d8a-8bcd-e37d24cf955e",
"name" : "银行参数",
"parent" : null,
"children" : [ ],
"icon" : "folder",
"isFolder" : true,
"sortNo" : null,
"isDefault" : false,
"pageType" : null,
"templateId" : null,
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2023-08-30 08:00:00",
"lastModifier" : "admin",
"lastModifyDate" : "2023-08-30 08:00:00",
"corporationCode" : null,
"id" : "BIII",
"name" : "BIII参数",
"parent" : null,
"children" : [ {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-26 17:34:22",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-26 17:34:22",
"corporationCode" : "_PRIMARY_",
"id" : "478b6c43-ca5b-45c4-a0cb-14715f45c98c",
"name" : "信用风险-表内风险权重参数表",
"parent" : "BIII",
"ticked" : true,
"children" : [ ],
"icon" : "calendar_month",
"isFolder" : false,
"sortNo" : 100,
"isDefault" : true,
"pageType" : "TEMPLATE",
"templateId" : "47442edb-7b0d-4dc4-ab71-755ddbae71bf",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : null,
"createDate" : null,
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-27 15:41:34",
"corporationCode" : "_PRIMARY_",
"id" : "BWFXQZ",
"name" : "信用风险-表外主体权重参数表",
"parent" : "BIII",
"children" : [ ],
"icon" : "calendar_today",
"isFolder" : false,
"sortNo" : 200,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "1b42b129-b683-4ac0-a8dc-3d39db1eb836",
"routeName" : null,
"routeQuery" : null
} ],
"icon" : "folder",
"isFolder" : true,
"sortNo" : 100,
"isDefault" : false,
"pageType" : null,
"templateId" : null,
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-28 13:56:36",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 13:56:36",
"corporationCode" : "_PRIMARY_",
"id" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"name" : "交易对手信用风险",
"parent" : null,
"children" : [ {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-28 14:03:45",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 14:03:45",
"corporationCode" : "_PRIMARY_",
"id" : "fac9819a-84a7-46bd-9e89-47e07ebeb31b",
"name" : "交易对手信用风险设置",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "settings_suggest",
"isFolder" : false,
"sortNo" : 100,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "61da07af-351f-4c70-9e58-0ab99ca1d730",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-28 14:55:57",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 14:55:57",
"corporationCode" : "_PRIMARY_",
"id" : "2389c168-12c5-4955-b31f-5090f9dcea4b",
"name" : "SACCR监管参数配置",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "settings_applications",
"isFolder" : false,
"sortNo" : 200,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "df6de052-7f56-42bd-895b-ca09088e0906",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : null,
"createDate" : null,
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 16:13:48",
"corporationCode" : "_PRIMARY_",
"id" : "ec48aefe-1181-4764-9a97-b149f20b520e",
"name" : "CVA行业类别映射",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "linear_scale",
"isFolder" : false,
"sortNo" : 300,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "125dc90f-15cb-4b82-961b-1b7361f0df5e",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : null,
"createDate" : null,
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 16:31:40",
"corporationCode" : "_PRIMARY_",
"id" : "a315ede8-3438-4d00-a60a-c9ef2babc0b1",
"name" : "货币优先顺序",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "currency_yen",
"isFolder" : false,
"sortNo" : 400,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "3fcee541-43f6-480e-827b-6cf021f748eb",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-28 16:45:56",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 16:45:56",
"corporationCode" : "_PRIMARY_",
"id" : "8865a5dd-0a88-4362-9ba9-192009072ad0",
"name" : "CVA评级等级映射",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "format_list_numbered",
"isFolder" : false,
"sortNo" : 500,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "637eea6b-1fcf-4977-a56b-9653b0703c31",
"routeName" : null,
"routeQuery" : null
}, {
"jpaVersion" : null,
"dataComeFrom" : "INPUT",
"creator" : "admin",
"createDate" : "2024-02-28 17:08:39",
"lastModifier" : "admin",
"lastModifyDate" : "2024-02-28 17:08:39",
"corporationCode" : "_PRIMARY_",
"id" : "62021407-ac21-4fde-a37d-e53865b94aa5",
"name" : "CVA权重映射",
"parent" : "bc56db9d-c4ad-4d66-9d55-7ae1a9ad3af3",
"children" : [ ],
"icon" : "percent",
"isFolder" : false,
"sortNo" : 600,
"isDefault" : false,
"pageType" : "TEMPLATE",
"templateId" : "dacd1de2-8492-4123-b1fb-f8abb4f6346b",
"routeName" : null,
"routeQuery" : null
} ],
"icon" : "folder",
"isFolder" : true,
"sortNo" : 300,
"isDefault" : false,
"pageType" : null,
"templateId" : null,
"routeName" : null,
"routeQuery" : null
} ]
}
}

5
io.sc.platform.jdbc.driver.oceanbase/build.gradle

@ -0,0 +1,5 @@
dependencies {
api(
"com.oceanbase:oceanbase-client:2.4.0",
)
}

0
io.sc.platform.jdbc.driver.oceanbase/gradle.properties

10
io.sc.platform.jdbc.driver.oceanbase/src/main/resources/META-INF/platform/plugins/jdbc-connection-template.json

@ -0,0 +1,10 @@
{
"installerEnable" : true,
"type" : "OceanBase",
"version" : "2.4",
"driver" : "com.oceanbase.jdbc.Driver",
"url" : "jdbc:oceanbase://${host}:${port}/${database}",
"urlSample" : "jdbc:oceanbase://localhost:2883/shemaname?pool=false",
"hibernateDialect" : "org.hibernate.dialect.Oracle10gDialect",
"validationQuery" : "select 1 from dual"
}

2
io.sc.platform.mvc.frontend/webpack.env.serve.cjs

@ -19,7 +19,7 @@ module.exports = (env)=> merge(common, mf,{
directory: path.join(__dirname, 'public'),
},
compress: false,
port: 3000,
port: 3001,
hot: true,
// 保证在出现 404 错误时,能够导航到 index.html
historyApiFallback: true,

Loading…
Cancel
Save