48 changed files with 1950 additions and 2905 deletions
@ -0,0 +1,38 @@ |
|||||
|
<template> |
||||
|
<template v-if="field.name"> |
||||
|
<component |
||||
|
:is="field.type" |
||||
|
:ref="(el) => form.setComponentRef(el, field.name)" |
||||
|
v-model="form.data[field.name]" |
||||
|
v-bind="field" |
||||
|
:form="form.instance" |
||||
|
:style="form.getFieldStyle(field)" |
||||
|
@update:model-value="form.cf.updateModelValue(field.name, form.data[field.name])" |
||||
|
></component> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<component |
||||
|
:is="field.type" |
||||
|
:ref="(el) => form.setComponentRef(el, field.name)" |
||||
|
v-bind="field" |
||||
|
:form="form.instance" |
||||
|
:style="form.getFieldStyle(field)" |
||||
|
></component> |
||||
|
</template> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { inject } from 'vue'; |
||||
|
import { Form } from './ts/Form'; |
||||
|
|
||||
|
const form = <Form>inject('form'); |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
field: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,35 @@ |
|||||
|
<template> |
||||
|
<template v-if="field.type === Constant.GROUP_TYPE"> |
||||
|
<FormGroup v-if="showFormGroupComputed" v-bind="field" :fields="field.fields" :style="form.getFieldStyle(field)"></FormGroup> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<Field :field="props.field"></Field> |
||||
|
</template> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { inject, computed } from 'vue'; |
||||
|
import { Form } from './ts/Form'; |
||||
|
import { Constant } from './ts/Constant'; |
||||
|
import Field from './Field.vue'; |
||||
|
import FormGroup from './FormGroup.vue'; |
||||
|
|
||||
|
const form = <Form>inject('form'); |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
field: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {}; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const showFormGroupComputed = computed(() => { |
||||
|
if (props.field.fields && Array.isArray(props.field.fields) && props.field.fields.length > 0) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,124 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<q-card flat bordered> |
||||
|
<q-item dense style="padding: 0px"> |
||||
|
<q-item-section> |
||||
|
<q-item-label header :style="headerStyleComputed"> |
||||
|
<q-icon v-if="props.icon" :name="props.icon" size="sm" v-bind="props.iconAttrs" /> |
||||
|
<span style="margin-left: 5px">{{ label }}</span> |
||||
|
</q-item-label> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
<q-separator /> |
||||
|
<q-card-section style="padding: 8px"> |
||||
|
<div class="grid" :style="layoutStyleComputed"> |
||||
|
<template v-for="(field, index) in fields as any" :key="String(index)"> |
||||
|
<FormElement :field="field"></FormElement> |
||||
|
</template> |
||||
|
</div> |
||||
|
</q-card-section> |
||||
|
</q-card> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { inject, computed } from 'vue'; |
||||
|
import { useQuasar } from 'quasar'; |
||||
|
import { Form } from './ts/Form'; |
||||
|
import FormElement from './FormElement.vue'; |
||||
|
|
||||
|
const $q = useQuasar(); |
||||
|
const form = <Form>inject('form'); |
||||
|
|
||||
|
// 颜色透明度 |
||||
|
const colorOpacity = 0.048; |
||||
|
const colors = [ |
||||
|
// 红色 |
||||
|
'rgba(255, 0, 0, ' + colorOpacity + ')', |
||||
|
// 橙色 |
||||
|
'rgba(255, 149, 0, ' + colorOpacity + ')', |
||||
|
// 黄色 |
||||
|
'rgba(255, 255, 0, ' + colorOpacity + ')', |
||||
|
// 绿色 |
||||
|
'rgba(4, 255, 0, ' + colorOpacity + ')', |
||||
|
// 青色 |
||||
|
'rgba(0, 255, 255, ' + colorOpacity + ')', |
||||
|
// 蓝色 |
||||
|
'rgba(0, 38, 255, ' + colorOpacity + ')', |
||||
|
// 紫色 |
||||
|
'rgba(255, 0, 247, ' + colorOpacity + ')', |
||||
|
// 灰色 |
||||
|
'rgba(0, 0, 0, ' + colorOpacity + ')', |
||||
|
]; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
// 分组标签名 |
||||
|
label: { type: String, default: '' }, |
||||
|
// 分组头背景色 |
||||
|
color: { type: String, default: undefined }, |
||||
|
// 分组图标 |
||||
|
icon: { type: String, default: undefined }, |
||||
|
// 分组图标其余属性 |
||||
|
iconAttrs: { type: Object, default: undefined }, |
||||
|
// 分组内一行放几个字段,与 form 保持一致,可配置具体值,也可配置屏幕断点显示值 |
||||
|
colsNum: { type: [Number, Object], default: 0 }, |
||||
|
// 字段集合 |
||||
|
fields: { |
||||
|
type: Array, |
||||
|
default: () => { |
||||
|
return []; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const headerStyleComputed = computed(() => { |
||||
|
const style = { |
||||
|
// 垂直居中 |
||||
|
display: 'flex', |
||||
|
alignItems: 'center', |
||||
|
// 文字颜色 |
||||
|
color: '#000', |
||||
|
padding: '5px', |
||||
|
}; |
||||
|
if (props.color) { |
||||
|
// 背景颜色 |
||||
|
if (props.color === 'auto') { |
||||
|
const index = form.fieldArray.value.findIndex((item) => item.label === props.label); |
||||
|
let colorIndex = index > -1 && index < colors.length ? index : getRandomInt(); |
||||
|
style['background-color'] = colors[colorIndex]; |
||||
|
} else { |
||||
|
style['background-color'] = props.color; |
||||
|
} |
||||
|
} |
||||
|
return style; |
||||
|
}); |
||||
|
|
||||
|
// 随机取颜色数组下标 |
||||
|
const getRandomInt = () => { |
||||
|
const min = 0; |
||||
|
const max = colors.length - 1; |
||||
|
return Math.floor(Math.random() * (max - min + 1) + min); |
||||
|
}; |
||||
|
|
||||
|
const screenColsNumComputed = computed(() => { |
||||
|
if (typeof props.colsNum === 'number' && props.colsNum > 0) { |
||||
|
return props.colsNum; |
||||
|
} else if (typeof props.colsNum === 'object') { |
||||
|
const screen = { ...form.cm.screenCols, ...props.colsNum }; |
||||
|
return screen[$q.screen.name]; |
||||
|
} |
||||
|
return form.cm.screenCols[$q.screen.name]; |
||||
|
}); |
||||
|
|
||||
|
const layoutStyleComputed = computed(() => { |
||||
|
const style = {}; |
||||
|
if (typeof props.colsNum === 'number' && props.colsNum > 0) { |
||||
|
style['grid-template-columns'] = 'repeat(' + props.colsNum + ', minmax(0, 1fr))'; |
||||
|
} else { |
||||
|
style['grid-template-columns'] = 'repeat(' + screenColsNumComputed.value + ', minmax(0, 1fr))'; |
||||
|
} |
||||
|
style['column-gap'] = form.props.xGap + 'px'; |
||||
|
style['row-gap'] = form.props.yGap + 'px'; |
||||
|
return style; |
||||
|
}); |
||||
|
</script> |
@ -1,389 +0,0 @@ |
|||||
<template> |
|
||||
<div v-show="showIfComputed"> |
|
||||
<q-field |
|
||||
ref="fieldRef" |
|
||||
:hide-bottom-space="true" |
|
||||
:hide-hint="true" |
|
||||
:outlined="true" |
|
||||
:dense="true" |
|
||||
v-bind="attrs" |
|
||||
:stack-label="stackLabelRef" |
|
||||
:rules="rulesComputed" |
|
||||
:readonly="readonlyIfComputed" |
|
||||
:disable="disableIfComputed" |
|
||||
style="position: relative" |
|
||||
@focus.stop.prevent="focus" |
|
||||
@blur.stop.prevent="blur" |
|
||||
> |
|
||||
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }}</template> |
|
||||
<template #control> |
|
||||
<div v-if="editable && toolbar" class="w-full pt-2 border-b border-b-gray-200"> |
|
||||
<Toolbar :source-code-editor="editorView" :user-defined-functions="userDefinedFunctions"></Toolbar> |
|
||||
</div> |
|
||||
<div |
|
||||
ref="codemirrorRef" |
|
||||
:style="`width:100%; padding-top: ${lineNumber ? 4 : 2}px; overflow: auto;`" |
|
||||
@focus.stop.prevent="() => {}" |
|
||||
@click.stop.prevent="() => {}" |
|
||||
></div> |
|
||||
</template> |
|
||||
<template v-if="!Tools.isEmpty(modelValueRef) && attrs.button" #append> |
|
||||
<q-btn |
|
||||
round |
|
||||
dense |
|
||||
flat |
|
||||
v-bind="attrs.button" |
|
||||
:style="{ |
|
||||
content: '', |
|
||||
position: 'absolute', |
|
||||
bottom: '5px', |
|
||||
right: '5px', |
|
||||
}" |
|
||||
@click.stop.prevent="buttonClick(attrs.button)" |
|
||||
/> |
|
||||
</template> |
|
||||
</q-field> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, useAttrs, onMounted, onUnmounted, onUpdated, watch, computed, toRaw } from 'vue'; |
|
||||
import { Tools } from '@/platform'; |
|
||||
import { FormValidators } from '@/platform/components'; |
|
||||
import { EditorView } from '@codemirror/view'; |
|
||||
import { EditorState, StateEffect, Compartment } from '@codemirror/state'; |
|
||||
import * as view from '@codemirror/view'; |
|
||||
import * as language from '@codemirror/language'; |
|
||||
import * as commands from '@codemirror/commands'; |
|
||||
import * as search from '@codemirror/search'; |
|
||||
import * as autocomplete from '@codemirror/autocomplete'; |
|
||||
|
|
||||
import { LanguageSupport } from '@codemirror/language'; |
|
||||
import { html, htmlLanguage } from '@codemirror/lang-html'; |
|
||||
import { java, javaLanguage } from '@codemirror/lang-java'; |
|
||||
import { javascript, javascriptLanguage } from '@codemirror/lang-javascript'; |
|
||||
import { json, jsonLanguage } from '@codemirror/lang-json'; |
|
||||
import { sql } from '@codemirror/lang-sql'; |
|
||||
import { xml, xmlLanguage } from '@codemirror/lang-xml'; |
|
||||
|
|
||||
import PlaceholderPlugin from './w-code-mirror/PlaceholderPlugin'; |
|
||||
import Toolbar from '@/platform/components/math/toolbar/Toolbar.vue'; |
|
||||
|
|
||||
const modelValueRef = defineModel({ type: String, default: '' }); |
|
||||
|
|
||||
const attrs = useAttrs(); |
|
||||
const rules = attrs.rules; |
|
||||
const fieldRef = ref(); |
|
||||
const props = defineProps({ |
|
||||
onChange: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
// 语言 |
|
||||
lang: { type: String, default: 'json' }, |
|
||||
// 宽度 |
|
||||
width: { type: [Number, String], default: '100%' }, |
|
||||
// 高度 |
|
||||
height: { type: [Number, String], default: undefined }, |
|
||||
// 行高 |
|
||||
lineHeight: { type: [Number, String], default: 20 }, |
|
||||
// 行数 |
|
||||
rows: { type: Number, default: 4 }, |
|
||||
// 是否包含工具条 |
|
||||
toolbar: { type: Boolean, default: true }, |
|
||||
// 是否可编辑 |
|
||||
editable: { type: Boolean, default: true }, |
|
||||
// 是否允许自动换行 |
|
||||
lineWrap: { type: Boolean, default: false }, |
|
||||
// tab size |
|
||||
tabSize: { type: Number, default: 4 }, |
|
||||
// 是否显示行号 |
|
||||
lineNumber: { type: Boolean, default: false }, |
|
||||
// 是否允许回车换行 |
|
||||
lineBreak: { type: Boolean, default: true }, |
|
||||
// 是否开启自动完成 |
|
||||
//autoCompletion: { type: Boolean, default: false }, |
|
||||
// 是否开启变量替换 |
|
||||
placeholder: { type: Boolean, default: false }, |
|
||||
autoCompletion: { |
|
||||
type: Function, |
|
||||
default: undefined, |
|
||||
}, |
|
||||
userDefinedFunctions: { |
|
||||
type: Array, |
|
||||
default: () => { |
|
||||
return []; |
|
||||
}, |
|
||||
}, |
|
||||
showIf: { |
|
||||
type: Function, |
|
||||
default: () => { |
|
||||
return true; |
|
||||
}, |
|
||||
}, |
|
||||
required: { |
|
||||
type: Boolean, |
|
||||
default: false, |
|
||||
}, |
|
||||
requiredIf: { |
|
||||
type: Function, |
|
||||
default: undefined, |
|
||||
}, |
|
||||
readOnlyIf: { |
|
||||
type: Function, |
|
||||
default: () => { |
|
||||
return false; |
|
||||
}, |
|
||||
}, |
|
||||
disableIf: { |
|
||||
type: Function, |
|
||||
default: () => { |
|
||||
return false; |
|
||||
}, |
|
||||
}, |
|
||||
form: { |
|
||||
type: Object, |
|
||||
default: undefined, |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
const rulesComputed = computed(() => { |
|
||||
let result = rules || <any>[]; |
|
||||
if (showIfComputed.value && requiredIfComputed.value) { |
|
||||
result.push(FormValidators.required()); |
|
||||
} else if (!showIfComputed.value) { |
|
||||
result = []; |
|
||||
} |
|
||||
if (fieldRef?.value) { |
|
||||
fieldRef.value.resetValidation(); |
|
||||
} |
|
||||
return result; |
|
||||
}); |
|
||||
|
|
||||
const showIfComputed = computed(() => { |
|
||||
return props.showIf({ |
|
||||
value: modelValueRef.value, |
|
||||
form: props.form, |
|
||||
}); |
|
||||
}); |
|
||||
const requiredIfComputed = computed(() => { |
|
||||
if (props.requiredIf) { |
|
||||
return ( |
|
||||
props.requiredIf({ |
|
||||
value: modelValueRef.value, |
|
||||
form: props.form, |
|
||||
}) || false |
|
||||
); |
|
||||
} else if (props.required) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
const readonlyIfComputed = computed(() => { |
|
||||
if (props.form && props.form.getStatus() === 'view') { |
|
||||
return true; |
|
||||
} |
|
||||
return props.readOnlyIf({ |
|
||||
value: modelValueRef.value, |
|
||||
form: props.form, |
|
||||
}); |
|
||||
}); |
|
||||
const disableIfComputed = computed(() => { |
|
||||
return props.disableIf({ |
|
||||
value: modelValueRef.value, |
|
||||
form: props.form, |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
const basicSetup = [ |
|
||||
EditorState.allowMultipleSelections.of(true), |
|
||||
|
|
||||
//view.lineNumbers(), |
|
||||
//view.highlightActiveLine(), |
|
||||
view.highlightActiveLineGutter(), |
|
||||
view.highlightSpecialChars(), |
|
||||
view.drawSelection(), |
|
||||
view.dropCursor(), |
|
||||
view.rectangularSelection(), |
|
||||
view.crosshairCursor(), |
|
||||
|
|
||||
commands.history(), |
|
||||
|
|
||||
//language.foldGutter(), |
|
||||
language.indentOnInput(), |
|
||||
language.syntaxHighlighting(language.defaultHighlightStyle, { fallback: true }), |
|
||||
language.bracketMatching(), |
|
||||
|
|
||||
//search.highlightSelectionMatches(), |
|
||||
|
|
||||
view.keymap.of([ |
|
||||
...commands.defaultKeymap, |
|
||||
...commands.historyKeymap, |
|
||||
...language.foldKeymap, |
|
||||
...autocomplete.completionKeymap, |
|
||||
...autocomplete.closeBracketsKeymap, |
|
||||
...search.searchKeymap, |
|
||||
commands.indentWithTab, |
|
||||
]), |
|
||||
]; |
|
||||
|
|
||||
const getLanguage = (lang: string): LanguageSupport => { |
|
||||
lang = lang || 'json'; |
|
||||
switch (lang.toLowerCase()) { |
|
||||
case 'html': |
|
||||
return html(); |
|
||||
case 'java': |
|
||||
return java(); |
|
||||
case 'javascript': |
|
||||
return javascript(); |
|
||||
case 'json': |
|
||||
return json(); |
|
||||
case 'sql': |
|
||||
return sql(); |
|
||||
case 'xml': |
|
||||
return xml(); |
|
||||
default: |
|
||||
return json(); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const codemirrorRef = ref(); |
|
||||
// q-field 的 stack-label 属性是对 field 的 label 进行设置效果 |
|
||||
// 如果 field 有值时, 无论是否获得焦点 label 都缩小显示 |
|
||||
// 如果 field 无值时, 如果 field 获得焦点 label 就缩小显示, 否则 label 放大显示 |
|
||||
const stackLabelRef = ref(!Tools.isUndefinedOrNull(modelValueRef.value)); |
|
||||
|
|
||||
let editorView; |
|
||||
let isFocus = false; |
|
||||
|
|
||||
if (props.lineNumber) { |
|
||||
// 行号 |
|
||||
basicSetup.push(view.lineNumbers()); |
|
||||
} |
|
||||
if (props.autoCompletion) { |
|
||||
// 自动完成 |
|
||||
basicSetup.push(autocomplete.closeBrackets()); |
|
||||
basicSetup.push( |
|
||||
autocomplete.autocompletion({ override: [props.autoCompletion], activateOnCompletion: props.activateOnCompletion, maxRenderedOptions: 1000 }), |
|
||||
); |
|
||||
} |
|
||||
if (props.placeholder) { |
|
||||
// 变量标识替换 |
|
||||
basicSetup.push(PlaceholderPlugin); |
|
||||
} |
|
||||
|
|
||||
if (props.lineWrap) { |
|
||||
basicSetup.push(EditorView.lineWrapping); |
|
||||
} |
|
||||
|
|
||||
// 是否可编辑 |
|
||||
basicSetup.push(EditorView.editable.of(props.editable)); |
|
||||
|
|
||||
onMounted(() => { |
|
||||
if (codemirrorRef.value) { |
|
||||
let language = new Compartment(); |
|
||||
let tabSize = new Compartment(); |
|
||||
editorView = new EditorView({ |
|
||||
extensions: [ |
|
||||
basicSetup, |
|
||||
language.of(getLanguage(props.lang)), |
|
||||
tabSize.of(EditorState.tabSize.of(props.tabSize)), |
|
||||
EditorState.readOnly.of(props.readOnlyIf(props.form)), |
|
||||
EditorView.theme({ |
|
||||
'&': { |
|
||||
outline: 'none !important', |
|
||||
width: Tools.px(props.width), |
|
||||
height: props.height ? Tools.px(props.height) : props.rows * 20 + (props.lineNumber ? 8 : 6) + 'px', |
|
||||
}, |
|
||||
'.cm-content': { |
|
||||
'line-height': props.lineHeight + 'px', |
|
||||
}, |
|
||||
}), |
|
||||
|
|
||||
// 以下代码可以添加内容变化后的操作, 为避免重复操作, 更新操作放到 blur 方法中了 |
|
||||
EditorView.updateListener.of(function (e) { |
|
||||
// emits('update:modelValue', e.state.doc.toString()); |
|
||||
let content = e.state.doc.toString(); |
|
||||
if (!props.lineBreak) { |
|
||||
content = content?.replace(/[\r\n]/g, ''); |
|
||||
} |
|
||||
//updateModelValue(content); |
|
||||
}), |
|
||||
], |
|
||||
parent: codemirrorRef.value, |
|
||||
doc: modelValueRef.value, |
|
||||
}); |
|
||||
watch( |
|
||||
() => modelValueRef.value, |
|
||||
() => { |
|
||||
// 当未获得焦点时,更新变更, 当获得焦点时不能更新 |
|
||||
if (!isFocus) { |
|
||||
let content = modelValueRef.value; |
|
||||
if (!props.lineBreak) { |
|
||||
content = content.replace(/[\r\n]/g, ''); |
|
||||
} |
|
||||
editorView.dispatch({ changes: { from: 0, to: editorView.state.doc.length, insert: content } }); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
onUnmounted(() => { |
|
||||
editorView.destroy(); |
|
||||
}); |
|
||||
|
|
||||
const focus = () => { |
|
||||
isFocus = true; |
|
||||
stackLabelRef.value = true; |
|
||||
}; |
|
||||
|
|
||||
const blur = () => { |
|
||||
isFocus = false; |
|
||||
let content = editorView.state.doc.toString(); |
|
||||
if (Tools.isUndefinedOrNull(content)) { |
|
||||
modelValueRef.value = ''; |
|
||||
} |
|
||||
const reg = /\$\{(.+?)\}\.\$\{(.+?)\}/g; |
|
||||
while (reg.test(content)) { |
|
||||
content = content.replace(reg, '${$1.$2}'); |
|
||||
} |
|
||||
modelValueRef.value = content; |
|
||||
if (content) { |
|
||||
stackLabelRef.value = true; |
|
||||
} else { |
|
||||
stackLabelRef.value = false; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const getValue = () => { |
|
||||
return editorView.state.doc.toString(); |
|
||||
}; |
|
||||
|
|
||||
const setValue = (value: string) => { |
|
||||
editorView.dispatch({ changes: { from: 0, to: editorView.state.doc.length, insert: value } }); |
|
||||
}; |
|
||||
|
|
||||
const configure = (values) => { |
|
||||
editorView.dispatch({ effects: StateEffect.reconfigure.of(values) }); |
|
||||
}; |
|
||||
|
|
||||
const buttonClick = (button) => { |
|
||||
if (button.click) { |
|
||||
button.click({ |
|
||||
value: modelValueRef.value, |
|
||||
form: props.form, |
|
||||
}); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const validate = () => { |
|
||||
return fieldRef.value.validate(); |
|
||||
}; |
|
||||
|
|
||||
defineExpose({ |
|
||||
getValue, |
|
||||
setValue, |
|
||||
configure, |
|
||||
validate, |
|
||||
}); |
|
||||
</script> |
|
@ -1,218 +0,0 @@ |
|||||
<template> |
|
||||
<div v-show="showIfComputed"> |
|
||||
<q-input |
|
||||
ref="cronRef" |
|
||||
v-model="valueRef" |
|
||||
:hide-bottom-space="true" |
|
||||
:hide-hint="true" |
|
||||
:outlined="true" |
|
||||
:dense="true" |
|
||||
v-bind="attrs" |
|
||||
:rules="rulesComputed" |
|
||||
:readonly="readonlyIfComputed" |
|
||||
:disable="disableIfComputed" |
|
||||
:v-bind="attrs" |
|
||||
@update:model-value="updateModelValue" |
|
||||
@change="changeValue" |
|
||||
> |
|
||||
<template #label> <span v-if="requiredIfComputed" style="color: red">*</span> {{ attrs.label }} </template> |
|
||||
<template v-if="!props.form || (props.form && props.form.getStatus() !== 'view')" #append> |
|
||||
<!-- 选择颜色按钮 --> |
|
||||
<q-btn icon="bi-pencil-square" padding="2px" flat square unelevated :title="$t('select')"> |
|
||||
<q-popup-proxy v-model:model-value="isShow" anchor="bottom right" self="top right" :offset="[0, 10]" style="width: 800px"> |
|
||||
<q-tabs v-model="selectedTab" no-caps> |
|
||||
<q-tab name="second" :label="$t('second')" /> |
|
||||
<q-tab name="minute" :label="$t('minute')" /> |
|
||||
<q-tab name="hour" :label="$t('hour')" /> |
|
||||
<q-tab name="day" :label="$t('day')" /> |
|
||||
<q-tab name="month" :label="$t('month')" /> |
|
||||
<q-tab name="week" :label="$t('week')" /> |
|
||||
<q-tab name="year" :label="$t('year')" /> |
|
||||
</q-tabs> |
|
||||
|
|
||||
<q-tab-panels v-model="selectedTab" animated swipeable vertical transition-prev="jump-up" transition-next="jump-up" :keep-alive="true"> |
|
||||
<q-tab-panel name="second"> |
|
||||
<SecondSegment v-model="secondValueRef" @update:model-value="valueChanged"></SecondSegment> |
|
||||
</q-tab-panel> |
|
||||
<q-tab-panel name="minute"> |
|
||||
<MinuteSegment v-model="minuteValueRef" @update:model-value="valueChanged"></MinuteSegment> |
|
||||
</q-tab-panel> |
|
||||
<q-tab-panel name="hour"> |
|
||||
<HourSegment v-model="hourValueRef" @update:model-value="valueChanged"></HourSegment> |
|
||||
</q-tab-panel> |
|
||||
<q-tab-panel name="day"> |
|
||||
<DaySegment v-model="dayValueRef" @update:model-value="valueChanged"></DaySegment> |
|
||||
</q-tab-panel> |
|
||||
<q-tab-panel name="month"> |
|
||||
<MonthSegment v-model="monthValueRef" @update:model-value="valueChanged"></MonthSegment> |
|
||||
</q-tab-panel> |
|
||||
<q-tab-panel name="week"> |
|
||||
<WeekSegment v-model="weekValueRef" @update:model-value="valueChanged"></WeekSegment> |
|
||||
</q-tab-panel> |
|
||||
<q-tab-panel name="year"> |
|
||||
<YearSegment v-model="yearValueRef" @update:model-value="valueChanged"></YearSegment> |
|
||||
</q-tab-panel> |
|
||||
</q-tab-panels> |
|
||||
</q-popup-proxy> |
|
||||
</q-btn> |
|
||||
</template> |
|
||||
</q-input> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref, useAttrs, watch, computed } from 'vue'; |
|
||||
import { Tools } from '@/platform'; |
|
||||
import { FormValidators } from '@/platform/components'; |
|
||||
import SecondSegment from './w-cron-segment/SecondSegment.vue'; |
|
||||
import MinuteSegment from './w-cron-segment/MinuteSegment.vue'; |
|
||||
import HourSegment from './w-cron-segment/HourSegment.vue'; |
|
||||
import DaySegment from './w-cron-segment/DaySegment.vue'; |
|
||||
import MonthSegment from './w-cron-segment/MonthSegment.vue'; |
|
||||
import WeekSegment from './w-cron-segment/WeekSegment.vue'; |
|
||||
import YearSegment from './w-cron-segment/YearSegment.vue'; |
|
||||
|
|
||||
const attrs = useAttrs(); |
|
||||
const rules = attrs.rules; |
|
||||
const cronRef = ref(); |
|
||||
const props = defineProps({ |
|
||||
onChange: { |
|
||||
type: Function, |
|
||||
default: () => {}, |
|
||||
}, |
|
||||
modelValue: { type: String, default: '' }, |
|
||||
showIf: { |
|
||||
type: Function, |
|
||||
default: () => { |
|
||||
return true; |
|
||||
}, |
|
||||
}, |
|
||||
required: { |
|
||||
type: Boolean, |
|
||||
default: false, |
|
||||
}, |
|
||||
requiredIf: { |
|
||||
type: Function, |
|
||||
default: undefined, |
|
||||
}, |
|
||||
readOnlyIf: { |
|
||||
type: Function, |
|
||||
default: () => { |
|
||||
return false; |
|
||||
}, |
|
||||
}, |
|
||||
disableIf: { |
|
||||
type: Function, |
|
||||
default: () => { |
|
||||
return false; |
|
||||
}, |
|
||||
}, |
|
||||
form: { |
|
||||
type: Object, |
|
||||
default: undefined, |
|
||||
}, |
|
||||
}); |
|
||||
const emit = defineEmits(['update:modelValue', 'change']); |
|
||||
|
|
||||
const isShow = ref(false); |
|
||||
const selectedTab = ref('second'); |
|
||||
|
|
||||
const valueRef = ref(props.modelValue || ''); |
|
||||
const splits = (props.modelValue || '').split(' '); |
|
||||
const secondValueRef = ref(splits[0]); |
|
||||
const minuteValueRef = ref(splits[1]); |
|
||||
const hourValueRef = ref(splits[2]); |
|
||||
const dayValueRef = ref(splits[3]); |
|
||||
const monthValueRef = ref(splits[4]); |
|
||||
const weekValueRef = ref(splits[5]); |
|
||||
const yearValueRef = ref(splits[6]); |
|
||||
|
|
||||
watch( |
|
||||
() => props.modelValue, |
|
||||
(newVal, oldVal) => { |
|
||||
valueRef.value = newVal; |
|
||||
}, |
|
||||
); |
|
||||
|
|
||||
const rulesComputed = computed(() => { |
|
||||
let result = rules || <any>[]; |
|
||||
if (showIfComputed.value && requiredIfComputed.value) { |
|
||||
result.push(FormValidators.required()); |
|
||||
} else if (!showIfComputed.value) { |
|
||||
result = []; |
|
||||
} |
|
||||
if (cronRef?.value) { |
|
||||
cronRef.value.resetValidation(); |
|
||||
} |
|
||||
return result; |
|
||||
}); |
|
||||
|
|
||||
const showIfComputed = computed(() => { |
|
||||
return props.showIf({ |
|
||||
value: valueRef.value, |
|
||||
form: props.form, |
|
||||
}); |
|
||||
}); |
|
||||
const requiredIfComputed = computed(() => { |
|
||||
if (props.requiredIf) { |
|
||||
return ( |
|
||||
props.requiredIf({ |
|
||||
value: valueRef.value, |
|
||||
form: props.form, |
|
||||
}) || false |
|
||||
); |
|
||||
} else if (props.required) { |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
}); |
|
||||
const readonlyIfComputed = computed(() => { |
|
||||
if (props.form && props.form.getStatus() === 'view') { |
|
||||
return true; |
|
||||
} |
|
||||
return props.readOnlyIf({ |
|
||||
value: valueRef.value, |
|
||||
form: props.form, |
|
||||
}); |
|
||||
}); |
|
||||
const disableIfComputed = computed(() => { |
|
||||
return props.disableIf({ |
|
||||
value: valueRef.value, |
|
||||
form: props.form, |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
const valueChanged = () => { |
|
||||
const values = []; |
|
||||
values.push(secondValueRef.value ? secondValueRef.value : '*'); |
|
||||
values.push(minuteValueRef.value ? minuteValueRef.value : '*'); |
|
||||
values.push(hourValueRef.value ? hourValueRef.value : '*'); |
|
||||
values.push(dayValueRef.value ? dayValueRef.value : '*'); |
|
||||
values.push(monthValueRef.value ? monthValueRef.value : '*'); |
|
||||
values.push(weekValueRef.value ? weekValueRef.value : '*'); |
|
||||
values.push(yearValueRef.value ? yearValueRef.value : '*'); |
|
||||
|
|
||||
const result = Tools.join(values, ' '); |
|
||||
valueRef.value = result; |
|
||||
emit('update:modelValue', result); |
|
||||
changeValue(result); |
|
||||
}; |
|
||||
|
|
||||
const updateModelValue = (value) => { |
|
||||
valueRef.value = value; |
|
||||
emit('update:modelValue', value); |
|
||||
}; |
|
||||
const changeValue = (value) => { |
|
||||
emit('change', { |
|
||||
value: value, |
|
||||
form: props.form, |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
const validate = () => { |
|
||||
return cronRef.value.validate(); |
|
||||
}; |
|
||||
|
|
||||
defineExpose({ |
|
||||
validate, |
|
||||
}); |
|
||||
</script> |
|
@ -1,76 +0,0 @@ |
|||||
import { EditorView, ViewPlugin, ViewUpdate, MatchDecorator, Decoration, DecorationSet, WidgetType } from '@codemirror/view'; |
|
||||
|
|
||||
class PlaceholderWidget extends WidgetType { |
|
||||
constructor(name) { |
|
||||
super(); |
|
||||
this.name = name; |
|
||||
} |
|
||||
|
|
||||
eq(other) { |
|
||||
return this.name == other.name; |
|
||||
} |
|
||||
|
|
||||
toDOM() { |
|
||||
const container = document.createElement('span'); |
|
||||
container.className = 'px-0.5'; |
|
||||
|
|
||||
const elt = document.createElement('span'); |
|
||||
elt.setAttribute('placeholder', true); |
|
||||
elt.className = 'p-0.5 border border-gray-800 rounded-md'; |
|
||||
elt.textContent = this.name; |
|
||||
|
|
||||
container.appendChild(elt); |
|
||||
return container; |
|
||||
} |
|
||||
|
|
||||
ignoreEvent() { |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const placeholderMatcher = new MatchDecorator({ |
|
||||
regexp: /\$\{(.+?)\}/g, |
|
||||
decoration: (match) => |
|
||||
Decoration.replace({ |
|
||||
widget: new PlaceholderWidget(match[1]), |
|
||||
}), |
|
||||
}); |
|
||||
|
|
||||
const placeholderPlugin = ViewPlugin.fromClass( |
|
||||
class { |
|
||||
placeholders: DecorationSet; |
|
||||
constructor(view: EditorView) { |
|
||||
this.placeholders = placeholderMatcher.createDeco(view); |
|
||||
} |
|
||||
update(update: ViewUpdate) { |
|
||||
this.placeholders = placeholderMatcher.updateDeco(update, this.placeholders); |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
decorations: (instance) => instance.placeholders, |
|
||||
provide: (plugin) => |
|
||||
EditorView.atomicRanges.of((view) => { |
|
||||
return view.plugin(plugin)?.placeholders || Decoration.none; |
|
||||
}), |
|
||||
eventHandlers: { |
|
||||
mouseover: (e, view) => { |
|
||||
const target = e.target as HTMLElement; |
|
||||
if (target.tagName.toLowerCase() === 'span' && target.getAttribute('placeholder')) { |
|
||||
target.className = 'p-0.5 border border-orange-400 rounded-md'; |
|
||||
} |
|
||||
}, |
|
||||
mouseout: (e, view) => { |
|
||||
const target = e.target as HTMLElement; |
|
||||
if (target.tagName.toLowerCase() === 'span' && target.getAttribute('placeholder')) { |
|
||||
target.className = 'p-0.5 border border-gray-800 rounded-md'; |
|
||||
} |
|
||||
}, |
|
||||
contextmenu: (e, view) => { |
|
||||
e.preventDefault(); |
|
||||
console.log(view); |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
); |
|
||||
|
|
||||
export default placeholderPlugin; |
|
@ -1,251 +0,0 @@ |
|||||
<template> |
|
||||
<q-list dense style="width: 100%"> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.day.per') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="notSpecify" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.day.notSpecify') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.day.period.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodFromRef" |
|
||||
type="number" |
|
||||
:disable="!periodFromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.day.period.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodToRef" |
|
||||
type="number" |
|
||||
:disable="!periodToEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.day.period.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per2" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.day.per2.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="per2FromRef" |
|
||||
type="number" |
|
||||
:disable="!per2FromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.day.per2.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="per2PerRef" |
|
||||
type="number" |
|
||||
:disable="!per2PerEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.day.per2.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
|
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="workDay" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.day.workDay.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="workDayRef" |
|
||||
type="number" |
|
||||
:disable="!workDayEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.day.workDay.2') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
|
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="lastDay" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.day.lastDay') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
|
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.day.specify') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row"> |
|
||||
<template v-for="index in 31" :key="index"> |
|
||||
<template v-if="index % 10 === 1"> |
|
||||
<div class="col-1"></div> |
|
||||
</template> |
|
||||
<div class="col-1"> |
|
||||
<q-checkbox |
|
||||
v-model="optionsRef[index - 1]" |
|
||||
dense |
|
||||
:label="'' + index" |
|
||||
:disable="!optionsEnableRef" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-checkbox> |
|
||||
</div> |
|
||||
<template v-if="index % 10 === 0"> |
|
||||
<div class="col-1"></div> |
|
||||
</template> |
|
||||
</template> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</q-list> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref } from 'vue'; |
|
||||
import { Tools } from '@/platform'; |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
modelValue: { type: String, default: '' }, |
|
||||
}); |
|
||||
const emit = defineEmits(['update:modelValue']); |
|
||||
|
|
||||
const valueRef = ref(props.modelValue); |
|
||||
const modeRef = ref(''); |
|
||||
|
|
||||
const periodFromEnableRef = ref(false); |
|
||||
const periodFromRef = ref(''); |
|
||||
|
|
||||
const periodToEnableRef = ref(false); |
|
||||
const periodToRef = ref(''); |
|
||||
|
|
||||
const per2FromEnableRef = ref(false); |
|
||||
const per2FromRef = ref(''); |
|
||||
|
|
||||
const per2PerEnableRef = ref(false); |
|
||||
const per2PerRef = ref(''); |
|
||||
|
|
||||
const workDayEnableRef = ref(false); |
|
||||
const workDayRef = ref(''); |
|
||||
|
|
||||
const optionsEnableRef = ref(false); |
|
||||
const optionsRef = ref([]); |
|
||||
for (let i = 0; i < 31; i++) { |
|
||||
optionsRef.value.push(false); |
|
||||
} |
|
||||
|
|
||||
const valueChanged = () => { |
|
||||
if (modeRef.value === 'per') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
workDayEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
periodFromEnableRef.value = true; |
|
||||
periodToEnableRef.value = true; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
workDayEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'per2') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = true; |
|
||||
per2PerEnableRef.value = true; |
|
||||
workDayEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'workDay') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
workDayEnableRef.value = true; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'lastDay') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
workDayEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
workDayEnableRef.value = false; |
|
||||
optionsEnableRef.value = true; |
|
||||
} |
|
||||
|
|
||||
if (modeRef.value === 'per') { |
|
||||
valueRef.value = '*'; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
valueRef.value = periodFromRef.value + '-' + periodToRef.value; |
|
||||
} else if (modeRef.value === 'per2') { |
|
||||
valueRef.value = per2FromRef.value + '/' + per2PerRef.value; |
|
||||
} else if (modeRef.value === 'workDay') { |
|
||||
valueRef.value = workDayRef.value + 'W'; |
|
||||
} else if (modeRef.value === 'lastDay') { |
|
||||
valueRef.value = 'L'; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
let seconds = []; |
|
||||
for (let i = 0; i < 31; i++) { |
|
||||
if (optionsRef.value[i]) { |
|
||||
seconds.push(i + 1); |
|
||||
} |
|
||||
} |
|
||||
valueRef.value = Tools.join(seconds, ','); |
|
||||
} |
|
||||
|
|
||||
emit('update:modelValue', valueRef.value); |
|
||||
}; |
|
||||
</script> |
|
@ -1,185 +0,0 @@ |
|||||
<template> |
|
||||
<q-list dense style="width: 100%"> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.hour.per') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.hour.period.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodFromRef" |
|
||||
type="number" |
|
||||
:disable="!periodFromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.hour.period.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodToRef" |
|
||||
type="number" |
|
||||
:disable="!periodToEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.hour.period.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per2" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.hour.per2.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="per2FromRef" |
|
||||
type="number" |
|
||||
:disable="!per2FromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.hour.per2.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="per2PerRef" |
|
||||
type="number" |
|
||||
:disable="!per2PerEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.hour.per2.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.hour.specify') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-item-label> |
|
||||
<div class="row"> |
|
||||
<template v-for="index in 24" :key="index"> |
|
||||
<template v-if="index % 8 === 1"> |
|
||||
<div class="col-1"></div> |
|
||||
</template> |
|
||||
<div class="col-1"> |
|
||||
<q-checkbox |
|
||||
v-model="optionsRef[index - 1]" |
|
||||
dense |
|
||||
:label="'' + (index - 1)" |
|
||||
:disable="!optionsEnableRef" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-checkbox> |
|
||||
</div> |
|
||||
<template v-if="index % 8 === 0"> |
|
||||
<div class="col-3"></div> |
|
||||
</template> |
|
||||
</template> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</q-list> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref } from 'vue'; |
|
||||
import { Tools } from '@/platform'; |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
modelValue: { type: String, default: '' }, |
|
||||
}); |
|
||||
const emit = defineEmits(['update:modelValue']); |
|
||||
|
|
||||
const valueRef = ref(props.modelValue); |
|
||||
const modeRef = ref(''); |
|
||||
|
|
||||
const periodFromEnableRef = ref(false); |
|
||||
const periodFromRef = ref(''); |
|
||||
|
|
||||
const periodToEnableRef = ref(false); |
|
||||
const periodToRef = ref(''); |
|
||||
|
|
||||
const per2FromEnableRef = ref(false); |
|
||||
const per2FromRef = ref(''); |
|
||||
|
|
||||
const per2PerEnableRef = ref(false); |
|
||||
const per2PerRef = ref(''); |
|
||||
|
|
||||
const optionsEnableRef = ref(false); |
|
||||
const optionsRef = ref([]); |
|
||||
for (let i = 0; i < 24; i++) { |
|
||||
optionsRef.value.push(false); |
|
||||
} |
|
||||
|
|
||||
const valueChanged = () => { |
|
||||
if (modeRef.value === 'per') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
periodFromEnableRef.value = true; |
|
||||
periodToEnableRef.value = true; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'per2') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = true; |
|
||||
per2PerEnableRef.value = true; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = true; |
|
||||
} |
|
||||
|
|
||||
if (modeRef.value === 'per') { |
|
||||
valueRef.value = '*'; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
valueRef.value = periodFromRef.value + '-' + periodToRef.value; |
|
||||
} else if (modeRef.value === 'per2') { |
|
||||
valueRef.value = per2FromRef.value + '/' + per2PerRef.value; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
let seconds = []; |
|
||||
for (let i = 0; i < 24; i++) { |
|
||||
if (optionsRef.value[i]) { |
|
||||
seconds.push(i); |
|
||||
} |
|
||||
} |
|
||||
valueRef.value = Tools.join(seconds, ','); |
|
||||
} |
|
||||
|
|
||||
emit('update:modelValue', valueRef.value); |
|
||||
}; |
|
||||
</script> |
|
@ -1,185 +0,0 @@ |
|||||
<template> |
|
||||
<q-list dense style="width: 100%"> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.minute.per') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.minute.period.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodFromRef" |
|
||||
type="number" |
|
||||
:disable="!periodFromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.minute.period.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodToRef" |
|
||||
type="number" |
|
||||
:disable="!periodToEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.minute.period.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per2" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.minute.per2.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="per2FromRef" |
|
||||
type="number" |
|
||||
:disable="!per2FromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.minute.per2.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="per2PerRef" |
|
||||
type="number" |
|
||||
:disable="!per2PerEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.minute.per2.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.minute.specify') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-item-label> |
|
||||
<div class="row"> |
|
||||
<template v-for="index in 60" :key="index"> |
|
||||
<template v-if="index % 10 === 1"> |
|
||||
<div class="col-1"></div> |
|
||||
</template> |
|
||||
<div class="col-1"> |
|
||||
<q-checkbox |
|
||||
v-model="optionsRef[index - 1]" |
|
||||
dense |
|
||||
:label="'' + (index - 1)" |
|
||||
:disable="!optionsEnableRef" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-checkbox> |
|
||||
</div> |
|
||||
<template v-if="index % 10 === 0"> |
|
||||
<div class="col-1"></div> |
|
||||
</template> |
|
||||
</template> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</q-list> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref } from 'vue'; |
|
||||
import { Tools } from '@/platform'; |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
modelValue: { type: String, default: '' }, |
|
||||
}); |
|
||||
const emit = defineEmits(['update:modelValue']); |
|
||||
|
|
||||
const valueRef = ref(props.modelValue); |
|
||||
const modeRef = ref(''); |
|
||||
|
|
||||
const periodFromEnableRef = ref(false); |
|
||||
const periodFromRef = ref(''); |
|
||||
|
|
||||
const periodToEnableRef = ref(false); |
|
||||
const periodToRef = ref(''); |
|
||||
|
|
||||
const per2FromEnableRef = ref(false); |
|
||||
const per2FromRef = ref(''); |
|
||||
|
|
||||
const per2PerEnableRef = ref(false); |
|
||||
const per2PerRef = ref(''); |
|
||||
|
|
||||
const optionsEnableRef = ref(false); |
|
||||
const optionsRef = ref([]); |
|
||||
for (let i = 0; i < 60; i++) { |
|
||||
optionsRef.value.push(false); |
|
||||
} |
|
||||
|
|
||||
const valueChanged = () => { |
|
||||
if (modeRef.value === 'per') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
periodFromEnableRef.value = true; |
|
||||
periodToEnableRef.value = true; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'per2') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = true; |
|
||||
per2PerEnableRef.value = true; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = true; |
|
||||
} |
|
||||
|
|
||||
if (modeRef.value === 'per') { |
|
||||
valueRef.value = '*'; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
valueRef.value = periodFromRef.value + '-' + periodToRef.value; |
|
||||
} else if (modeRef.value === 'per2') { |
|
||||
valueRef.value = per2FromRef.value + '/' + per2PerRef.value; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
let seconds = []; |
|
||||
for (let i = 0; i < 60; i++) { |
|
||||
if (optionsRef.value[i]) { |
|
||||
seconds.push(i); |
|
||||
} |
|
||||
} |
|
||||
valueRef.value = Tools.join(seconds, ','); |
|
||||
} |
|
||||
|
|
||||
emit('update:modelValue', valueRef.value); |
|
||||
}; |
|
||||
</script> |
|
@ -1,197 +0,0 @@ |
|||||
<template> |
|
||||
<q-list dense style="width: 100%"> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.month.per') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="notSpecify" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.month.notSpecify') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.month.period.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodFromRef" |
|
||||
type="number" |
|
||||
:disable="!periodFromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.month.period.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodToRef" |
|
||||
type="number" |
|
||||
:disable="!periodToEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.month.period.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per2" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.month.per2.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="per2FromRef" |
|
||||
type="number" |
|
||||
:disable="!per2FromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.month.per2.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="per2PerRef" |
|
||||
type="number" |
|
||||
:disable="!per2PerEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.month.per2.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
|
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.month.specify') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row"> |
|
||||
<template v-for="index in 12" :key="index"> |
|
||||
<template v-if="index % 6 === 1"> |
|
||||
<div class="col-1"></div> |
|
||||
</template> |
|
||||
<div class="col-1"> |
|
||||
<q-checkbox |
|
||||
v-model="optionsRef[index - 1]" |
|
||||
dense |
|
||||
:label="'' + index" |
|
||||
:disable="!optionsEnableRef" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-checkbox> |
|
||||
</div> |
|
||||
<template v-if="index % 6 === 0"> |
|
||||
<div class="col-5"></div> |
|
||||
</template> |
|
||||
</template> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</q-list> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref } from 'vue'; |
|
||||
import { Tools } from '@/platform'; |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
modelValue: { type: String, default: '' }, |
|
||||
}); |
|
||||
const emit = defineEmits(['update:modelValue']); |
|
||||
|
|
||||
const valueRef = ref(props.modelValue); |
|
||||
const modeRef = ref(''); |
|
||||
|
|
||||
const periodFromEnableRef = ref(false); |
|
||||
const periodFromRef = ref(''); |
|
||||
|
|
||||
const periodToEnableRef = ref(false); |
|
||||
const periodToRef = ref(''); |
|
||||
|
|
||||
const per2FromEnableRef = ref(false); |
|
||||
const per2FromRef = ref(''); |
|
||||
|
|
||||
const per2PerEnableRef = ref(false); |
|
||||
const per2PerRef = ref(''); |
|
||||
|
|
||||
const workDayEnableRef = ref(false); |
|
||||
const workDayRef = ref(''); |
|
||||
|
|
||||
const optionsEnableRef = ref(false); |
|
||||
const optionsRef = ref([]); |
|
||||
for (let i = 0; i < 12; i++) { |
|
||||
optionsRef.value.push(false); |
|
||||
} |
|
||||
|
|
||||
const valueChanged = () => { |
|
||||
if (modeRef.value === 'per') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
periodFromEnableRef.value = true; |
|
||||
periodToEnableRef.value = true; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'per2') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = true; |
|
||||
per2PerEnableRef.value = true; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = true; |
|
||||
} |
|
||||
|
|
||||
if (modeRef.value === 'per') { |
|
||||
valueRef.value = '*'; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
valueRef.value = periodFromRef.value + '-' + periodToRef.value; |
|
||||
} else if (modeRef.value === 'per2') { |
|
||||
valueRef.value = per2FromRef.value + '/' + per2PerRef.value; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
let seconds = []; |
|
||||
for (let i = 0; i < 12; i++) { |
|
||||
if (optionsRef.value[i]) { |
|
||||
seconds.push(i + 1); |
|
||||
} |
|
||||
} |
|
||||
valueRef.value = Tools.join(seconds, ','); |
|
||||
} |
|
||||
|
|
||||
emit('update:modelValue', valueRef.value); |
|
||||
}; |
|
||||
</script> |
|
@ -1,185 +0,0 @@ |
|||||
<template> |
|
||||
<q-list dense style="width: 100%"> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.second.per') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.second.period.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodFromRef" |
|
||||
type="number" |
|
||||
:disable="!periodFromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.second.period.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodToRef" |
|
||||
type="number" |
|
||||
:disable="!periodToEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.second.period.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per2" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.second.per2.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="per2FromRef" |
|
||||
type="number" |
|
||||
:disable="!per2FromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.second.per2.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="per2PerRef" |
|
||||
type="number" |
|
||||
:disable="!per2PerEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.second.per2.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.second.specify') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-item-label> |
|
||||
<div class="row"> |
|
||||
<template v-for="index in 60" :key="index"> |
|
||||
<template v-if="index % 10 === 1"> |
|
||||
<div class="col-1"></div> |
|
||||
</template> |
|
||||
<div class="col-1"> |
|
||||
<q-checkbox |
|
||||
v-model="optionsRef[index - 1]" |
|
||||
dense |
|
||||
:label="'' + (index - 1)" |
|
||||
:disable="!optionsEnableRef" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-checkbox> |
|
||||
</div> |
|
||||
<template v-if="index % 10 === 0"> |
|
||||
<div class="col-1"></div> |
|
||||
</template> |
|
||||
</template> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</q-list> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref } from 'vue'; |
|
||||
import { Tools } from '@/platform'; |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
modelValue: { type: String, default: '' }, |
|
||||
}); |
|
||||
const emit = defineEmits(['update:modelValue']); |
|
||||
|
|
||||
const valueRef = ref(props.modelValue); |
|
||||
const modeRef = ref(''); |
|
||||
|
|
||||
const periodFromEnableRef = ref(false); |
|
||||
const periodFromRef = ref(''); |
|
||||
|
|
||||
const periodToEnableRef = ref(false); |
|
||||
const periodToRef = ref(''); |
|
||||
|
|
||||
const per2FromEnableRef = ref(false); |
|
||||
const per2FromRef = ref(''); |
|
||||
|
|
||||
const per2PerEnableRef = ref(false); |
|
||||
const per2PerRef = ref(''); |
|
||||
|
|
||||
const optionsEnableRef = ref(false); |
|
||||
const optionsRef = ref([]); |
|
||||
for (let i = 0; i < 60; i++) { |
|
||||
optionsRef.value.push(false); |
|
||||
} |
|
||||
|
|
||||
const valueChanged = () => { |
|
||||
if (modeRef.value === 'per') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
periodFromEnableRef.value = true; |
|
||||
periodToEnableRef.value = true; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'per2') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = true; |
|
||||
per2PerEnableRef.value = true; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
per2FromEnableRef.value = false; |
|
||||
per2PerEnableRef.value = false; |
|
||||
optionsEnableRef.value = true; |
|
||||
} |
|
||||
|
|
||||
if (modeRef.value === 'per') { |
|
||||
valueRef.value = '*'; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
valueRef.value = periodFromRef.value + '-' + periodToRef.value; |
|
||||
} else if (modeRef.value === 'per2') { |
|
||||
valueRef.value = per2FromRef.value + '/' + per2PerRef.value; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
let seconds = []; |
|
||||
for (let i = 0; i < 60; i++) { |
|
||||
if (optionsRef.value[i]) { |
|
||||
seconds.push(i); |
|
||||
} |
|
||||
} |
|
||||
valueRef.value = Tools.join(seconds, ','); |
|
||||
} |
|
||||
|
|
||||
emit('update:modelValue', valueRef.value); |
|
||||
}; |
|
||||
</script> |
|
@ -1,233 +0,0 @@ |
|||||
<template> |
|
||||
<q-list dense style="width: 100%"> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.week.per') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="notSpecify" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.week.notSpecify') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.week.period.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodFromRef" |
|
||||
type="number" |
|
||||
:disable="!periodFromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.week.period.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodToRef" |
|
||||
type="number" |
|
||||
:disable="!periodToEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.week.period.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="weekOfYear" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.week.weekOfYear.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="weekOfYearRef" |
|
||||
type="number" |
|
||||
:disable="!weekOfYearEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.week.weekOfYear.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="dayOfWeekRef" |
|
||||
type="number" |
|
||||
:disable="!dayOfWeekEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.week.weekOfYear.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
|
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="lastWeek" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.week.lastDay.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="lastWeekRef" |
|
||||
type="number" |
|
||||
:disable="!lastWeekEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.week.lastDay.2') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
|
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="specify" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.week.specify') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row"> |
|
||||
<template v-for="index in 7" :key="index"> |
|
||||
<template v-if="index % 7 === 1"> |
|
||||
<div class="col-1"></div> |
|
||||
</template> |
|
||||
<div class="col-1"> |
|
||||
<q-checkbox |
|
||||
v-model="optionsRef[index - 1]" |
|
||||
dense |
|
||||
:label="'' + index" |
|
||||
:disable="!optionsEnableRef" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-checkbox> |
|
||||
</div> |
|
||||
<template v-if="index % 7 === 0"> |
|
||||
<div class="col-5"></div> |
|
||||
</template> |
|
||||
</template> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</q-list> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref } from 'vue'; |
|
||||
import { Tools } from '@/platform'; |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
modelValue: { type: String, default: '' }, |
|
||||
}); |
|
||||
const emit = defineEmits(['update:modelValue']); |
|
||||
|
|
||||
const valueRef = ref(props.modelValue); |
|
||||
const modeRef = ref(''); |
|
||||
|
|
||||
const periodFromEnableRef = ref(false); |
|
||||
const periodFromRef = ref(''); |
|
||||
|
|
||||
const periodToEnableRef = ref(false); |
|
||||
const periodToRef = ref(''); |
|
||||
|
|
||||
const weekOfYearEnableRef = ref(false); |
|
||||
const weekOfYearRef = ref(''); |
|
||||
|
|
||||
const dayOfWeekEnableRef = ref(false); |
|
||||
const dayOfWeekRef = ref(''); |
|
||||
|
|
||||
const lastWeekEnableRef = ref(false); |
|
||||
const lastWeekRef = ref(''); |
|
||||
|
|
||||
const optionsEnableRef = ref(false); |
|
||||
const optionsRef = ref([]); |
|
||||
for (let i = 0; i < 7; i++) { |
|
||||
optionsRef.value.push(false); |
|
||||
} |
|
||||
|
|
||||
const valueChanged = () => { |
|
||||
if (modeRef.value === 'per') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
weekOfYearEnableRef.value = false; |
|
||||
dayOfWeekEnableRef.value = false; |
|
||||
lastWeekEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
periodFromEnableRef.value = true; |
|
||||
periodToEnableRef.value = true; |
|
||||
weekOfYearEnableRef.value = false; |
|
||||
dayOfWeekEnableRef.value = false; |
|
||||
lastWeekEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'weekOfYear') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
weekOfYearEnableRef.value = true; |
|
||||
dayOfWeekEnableRef.value = true; |
|
||||
lastWeekEnableRef.value = false; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'lastWeek') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
weekOfYearEnableRef.value = false; |
|
||||
dayOfWeekEnableRef.value = false; |
|
||||
lastWeekEnableRef.value = true; |
|
||||
optionsEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
weekOfYearEnableRef.value = false; |
|
||||
dayOfWeekEnableRef.value = false; |
|
||||
lastWeekEnableRef.value = false; |
|
||||
optionsEnableRef.value = true; |
|
||||
} |
|
||||
|
|
||||
if (modeRef.value === 'per') { |
|
||||
valueRef.value = '*'; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
valueRef.value = periodFromRef.value + '-' + periodToRef.value; |
|
||||
} else if (modeRef.value === 'weekOfYear') { |
|
||||
valueRef.value = weekOfYearRef.value + '/' + dayOfWeekRef.value; |
|
||||
} else if (modeRef.value === 'lastWeek') { |
|
||||
valueRef.value = lastWeekRef.value + 'L'; |
|
||||
} else if (modeRef.value === 'specify') { |
|
||||
let seconds = []; |
|
||||
for (let i = 0; i < 7; i++) { |
|
||||
if (optionsRef.value[i]) { |
|
||||
seconds.push(i + 1); |
|
||||
} |
|
||||
} |
|
||||
valueRef.value = Tools.join(seconds, ','); |
|
||||
} |
|
||||
|
|
||||
emit('update:modelValue', valueRef.value); |
|
||||
}; |
|
||||
</script> |
|
@ -1,93 +0,0 @@ |
|||||
<template> |
|
||||
<q-list dense style="width: 100%"> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="per" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.year.per') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="notSpecify" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label>{{ $t('cron.year.notSpecify') }}</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
<q-item> |
|
||||
<q-item-section avatar> |
|
||||
<q-radio v-model="modeRef" val="period" @update:model-value="valueChanged" /> |
|
||||
</q-item-section> |
|
||||
<q-item-section> |
|
||||
<q-item-label> |
|
||||
<div class="row no-wrap items-center"> |
|
||||
<span class="pr-2">{{ $t('cron.year.period.1') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodFromRef" |
|
||||
type="number" |
|
||||
:disable="!periodFromEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.year.period.2') }}</span> |
|
||||
<q-input |
|
||||
v-model="periodToRef" |
|
||||
type="number" |
|
||||
:disable="!periodToEnableRef" |
|
||||
outlined |
|
||||
dense |
|
||||
style="width: 100px" |
|
||||
@update:model-value="valueChanged" |
|
||||
></q-input> |
|
||||
<span class="px-2">{{ $t('cron.year.period.3') }}</span> |
|
||||
</div> |
|
||||
</q-item-label> |
|
||||
</q-item-section> |
|
||||
</q-item> |
|
||||
</q-list> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
import { ref } from 'vue'; |
|
||||
import { Tools } from '@/platform'; |
|
||||
|
|
||||
const props = defineProps({ |
|
||||
modelValue: { type: String, default: '' }, |
|
||||
}); |
|
||||
const emit = defineEmits(['update:modelValue']); |
|
||||
|
|
||||
const valueRef = ref(props.modelValue); |
|
||||
const modeRef = ref(''); |
|
||||
|
|
||||
const periodFromEnableRef = ref(false); |
|
||||
const periodFromRef = ref(''); |
|
||||
|
|
||||
const periodToEnableRef = ref(false); |
|
||||
const periodToRef = ref(''); |
|
||||
|
|
||||
const valueChanged = () => { |
|
||||
if (modeRef.value === 'per') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'notSpecify') { |
|
||||
periodFromEnableRef.value = false; |
|
||||
periodToEnableRef.value = false; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
periodFromEnableRef.value = true; |
|
||||
periodToEnableRef.value = true; |
|
||||
} |
|
||||
|
|
||||
if (modeRef.value === 'per') { |
|
||||
valueRef.value = '*'; |
|
||||
} else if (modeRef.value === 'notSpecify') { |
|
||||
valueRef.value = ''; |
|
||||
} else if (modeRef.value === 'period') { |
|
||||
valueRef.value = periodFromRef.value + '-' + periodToRef.value; |
|
||||
} |
|
||||
|
|
||||
emit('update:modelValue', valueRef.value); |
|
||||
}; |
|
||||
</script> |
|
@ -1,5 +0,0 @@ |
|||||
<template> |
|
||||
<div></div> |
|
||||
</template> |
|
||||
<script setup lang="ts"> |
|
||||
</script> |
|
@ -0,0 +1,47 @@ |
|||||
|
import { Screen } from 'quasar'; |
||||
|
import { computed } from 'vue'; |
||||
|
import { Form } from './Form'; |
||||
|
|
||||
|
/** |
||||
|
* 计算属性管理器 |
||||
|
*/ |
||||
|
export class ComputedManager { |
||||
|
form: Form; |
||||
|
|
||||
|
/** |
||||
|
* 不同屏幕断点下一行显示的字段数量 |
||||
|
*/ |
||||
|
screenCols = { xs: 1, sm: 2, md: 3, lg: 4, xl: 6 }; |
||||
|
|
||||
|
constructor(form_: Form) { |
||||
|
this.form = form_; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据屏幕断点,获得当前一行可显示的字段数量 |
||||
|
*/ |
||||
|
screenColsNumComputed = computed(() => { |
||||
|
if (typeof this.form.props.colsNum === 'number' && this.form.props.colsNum > 0) { |
||||
|
return this.form.props.colsNum; |
||||
|
} else if (typeof this.form.props.colsNum === 'object') { |
||||
|
const screen = { ...this.screenCols, ...this.form.props.colsNum }; |
||||
|
return screen[Screen.name]; |
||||
|
} |
||||
|
return this.screenCols[Screen.name]; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 表单布局样式 |
||||
|
*/ |
||||
|
formLayoutStyleComputed = computed(() => { |
||||
|
const style = {}; |
||||
|
if (typeof this.form.props.colsNum === 'number' && this.form.props.colsNum > 0) { |
||||
|
style['grid-template-columns'] = 'repeat(' + this.form.props.colsNum + ', minmax(0, 1fr))'; |
||||
|
} else { |
||||
|
style['grid-template-columns'] = 'repeat(' + this.screenColsNumComputed.value + ', minmax(0, 1fr))'; |
||||
|
} |
||||
|
style['column-gap'] = this.form.props.xGap + 'px'; |
||||
|
style['row-gap'] = this.form.props.yGap + 'px'; |
||||
|
return style; |
||||
|
}); |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
/** |
||||
|
* 常量 |
||||
|
*/ |
||||
|
export class Constant { |
||||
|
/** |
||||
|
* 新增状态 |
||||
|
*/ |
||||
|
static STATUS_ADD = 'add'; |
||||
|
|
||||
|
/** |
||||
|
* 用于分组的字段类型 |
||||
|
*/ |
||||
|
static GROUP_TYPE = 'w-form-group'; |
||||
|
} |
@ -0,0 +1,189 @@ |
|||||
|
import { toRaw } from 'vue'; |
||||
|
import { Tools, $t } from '@/platform'; |
||||
|
import { getDefaultValue } from '../FormField.ts'; |
||||
|
import { Form } from './Form.ts'; |
||||
|
import { ErrorType } from './types/ErrorType.ts'; |
||||
|
|
||||
|
/** |
||||
|
* 对外暴露API管理器 |
||||
|
*/ |
||||
|
export class ExposeApiManager { |
||||
|
form: Form; |
||||
|
|
||||
|
constructor(form_: Form) { |
||||
|
this.form = form_; |
||||
|
|
||||
|
this.getData = this.getData.bind(this); |
||||
|
this.setData = this.setData.bind(this); |
||||
|
this.getStatus = this.getStatus.bind(this); |
||||
|
this.setStatus = this.setStatus.bind(this); |
||||
|
this.reset = this.reset.bind(this); |
||||
|
this.validate = this.validate.bind(this); |
||||
|
this.setFieldValue = this.setFieldValue.bind(this); |
||||
|
this.getFieldValue = this.getFieldValue.bind(this); |
||||
|
this.getColsNum = this.getColsNum.bind(this); |
||||
|
this.setValidationErrors = this.setValidationErrors.bind(this); |
||||
|
this.getFieldComponent = this.getFieldComponent.bind(this); |
||||
|
this.resetValidation = this.resetValidation.bind(this); |
||||
|
this.getFields = this.getFields.bind(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取表单数据 |
||||
|
* @returns |
||||
|
*/ |
||||
|
getData() { |
||||
|
const data = { ...toRaw(this.form.data) }; |
||||
|
if (!Tools.isEmpty(this.form.props.modelValue)) { |
||||
|
// 对于用户自定义绑定模型值的,为了避免页面未填写值的字段不会更新到模型值中,返回数据值时将其合并。
|
||||
|
const modelValue = this.form.buildModelValueByFields(); |
||||
|
return { ...modelValue, ...data }; |
||||
|
} |
||||
|
return data; |
||||
|
} |
||||
|
/** |
||||
|
* 设置表单数据 |
||||
|
* @param data 数据对象(JSON格式) |
||||
|
*/ |
||||
|
setData(data: any) { |
||||
|
Object.keys(this.form.fields).forEach((key: string) => { |
||||
|
if (Tools.hasOwnProperty(this.form.data, key)) { |
||||
|
if (Tools.isEmpty(data)) { |
||||
|
this.form.data[key] = getDefaultValue(this.form.fields[key]); |
||||
|
} else if (data[key] !== undefined) { |
||||
|
this.form.data[key] = data[key]; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取表单状态 |
||||
|
* @returns |
||||
|
*/ |
||||
|
getStatus() { |
||||
|
return this.form.status; |
||||
|
} |
||||
|
/** |
||||
|
* 设置表单状态 |
||||
|
* @param status |
||||
|
*/ |
||||
|
setStatus(status: string) { |
||||
|
this.form.status = status; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重置表单 |
||||
|
*/ |
||||
|
reset() { |
||||
|
Object.keys(this.form.data).forEach((key) => { |
||||
|
this.form.data[key] = getDefaultValue(this.form.fields[key]); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 表单验证 |
||||
|
*/ |
||||
|
async validate() { |
||||
|
const result = await this.formValidate(); |
||||
|
return result; |
||||
|
} |
||||
|
private async formValidate() { |
||||
|
let validate = false; |
||||
|
await this.form.cf |
||||
|
.getFormRef() |
||||
|
.validate() |
||||
|
.then((success) => { |
||||
|
if (success) { |
||||
|
validate = true; |
||||
|
} |
||||
|
}); |
||||
|
return validate; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置字段值 |
||||
|
* @param fieldName |
||||
|
* @param value |
||||
|
*/ |
||||
|
setFieldValue(fieldName: string, value: any) { |
||||
|
this.form.data[fieldName] = value; |
||||
|
} |
||||
|
/** |
||||
|
* 获取字段值 |
||||
|
* @param fieldName |
||||
|
* @returns |
||||
|
*/ |
||||
|
getFieldValue(fieldName: string) { |
||||
|
return this.form.data[fieldName]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取当前表单一行应该显示的元素个数 |
||||
|
* @returns |
||||
|
*/ |
||||
|
getColsNum() { |
||||
|
return this.form.cm.screenColsNumComputed.value; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置后台校验错误信息 |
||||
|
* @param errors |
||||
|
*/ |
||||
|
setValidationErrors(errors: ErrorType[]) { |
||||
|
if (errors && errors.length > 0) { |
||||
|
const grouped = {}; |
||||
|
errors.map(({ fieldName, ...rest }) => { |
||||
|
grouped[fieldName] = grouped[fieldName] || []; |
||||
|
grouped[fieldName].push(rest); |
||||
|
}); |
||||
|
for (const name in grouped) { |
||||
|
const field = this.form.fieldArrayFindByName(name); |
||||
|
if (field) { |
||||
|
field.error = true; |
||||
|
field.errorMessage = grouped[name] |
||||
|
.map((obj) => { |
||||
|
if (!Tools.isEmpty(obj['errorMessageI18nKey'])) { |
||||
|
return $t(obj['errorMessageI18nKey']); |
||||
|
} |
||||
|
return obj.errorMessage; |
||||
|
}) |
||||
|
.join('、'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取字段元素使用的组件实例 |
||||
|
* @param fieldName |
||||
|
* @returns |
||||
|
*/ |
||||
|
getFieldComponent(fieldName: string) { |
||||
|
return this.form.componentRef[fieldName]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重置验证 |
||||
|
*/ |
||||
|
resetValidation() { |
||||
|
this.form.cf.getFormRef().resetValidation(); |
||||
|
this.form.fieldArrayRecursiveHandler(this.form.fieldArray.value, (field: any) => { |
||||
|
if (Tools.hasOwnProperty(field, 'error') && field['error']) { |
||||
|
field['error'] = false; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取所有字段元素 |
||||
|
* @returns |
||||
|
*/ |
||||
|
getFields() { |
||||
|
const result = {}; |
||||
|
this.form.fieldArrayRecursiveHandler(this.form.fieldArray.value, (field: any) => { |
||||
|
result[field.name] = field; |
||||
|
}); |
||||
|
return result; |
||||
|
} |
||||
|
} |
@ -0,0 +1,229 @@ |
|||||
|
import { Reactive, Ref, ref, reactive } from 'vue'; |
||||
|
import { Tools } from '@/platform'; |
||||
|
import { getDefaultValue } from '../FormField.ts'; |
||||
|
import { Constant } from './Constant.ts'; |
||||
|
import { PropsType } from './types/PropsType.ts'; |
||||
|
import { ComputedManager } from './ComputedManager.ts'; |
||||
|
import { ExposeApiManager } from './ExposeApiManager.ts'; |
||||
|
import { ComponentFunctionsType } from './types/ComponentFunctionsType.ts'; |
||||
|
|
||||
|
/** |
||||
|
* w-form组件类 |
||||
|
*/ |
||||
|
export class Form { |
||||
|
/** |
||||
|
* 组件实例 |
||||
|
*/ |
||||
|
instance?: any; |
||||
|
/** |
||||
|
* 配置属性 |
||||
|
*/ |
||||
|
props: PropsType; |
||||
|
/** |
||||
|
* 表单数据 |
||||
|
*/ |
||||
|
data: Reactive<any> = reactive({}); |
||||
|
/** |
||||
|
* 字段集合数组 |
||||
|
*/ |
||||
|
fieldArray: Ref = ref([]); |
||||
|
/** |
||||
|
* 字段集合对象,方便快速获取字段信息( name 为 key,字段配置为 value ,排除无 name 配置的元素(如 w-form-group 类型)。 |
||||
|
*/ |
||||
|
fields: Reactive<any> = reactive({}); |
||||
|
/** |
||||
|
* 表单状态(用以标识表单当前是新增、编辑、复制等不同状态,默认为新增,可通过 setStatus 方法设置,getStatus 方法获得) |
||||
|
*/ |
||||
|
status: string = Constant.STATUS_ADD; |
||||
|
/** |
||||
|
* 字段使用的组件 ref 对象(渲染时动态绑定 ref ,key 为字段 name ) |
||||
|
*/ |
||||
|
componentRef: Reactive<any> = reactive({}); |
||||
|
/** |
||||
|
* 计算属性管理器 |
||||
|
*/ |
||||
|
cm: ComputedManager; |
||||
|
/** |
||||
|
* 对外暴露API管理器 |
||||
|
*/ |
||||
|
api: ExposeApiManager; |
||||
|
/** |
||||
|
* vue文件中定义的函数 |
||||
|
*/ |
||||
|
cf: ComponentFunctionsType = { |
||||
|
getFormRef: () => {}, |
||||
|
updateModelValue: () => {}, |
||||
|
}; |
||||
|
|
||||
|
setInstance(instance_: any) { |
||||
|
this.instance = instance_; |
||||
|
} |
||||
|
|
||||
|
constructor(props_: PropsType) { |
||||
|
this.props = props_; |
||||
|
// 初始化
|
||||
|
this.cm = new ComputedManager(this); |
||||
|
this.api = new ExposeApiManager(this); |
||||
|
if (this.props.fields) { |
||||
|
this.buildFieldsAndData(); |
||||
|
} |
||||
|
|
||||
|
this.getFieldStyle = this.getFieldStyle.bind(this); |
||||
|
this.setComponentRef = this.setComponentRef.bind(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置字段组件的 ref |
||||
|
* @param el html元素对象 |
||||
|
* @param fieldName 字段名 |
||||
|
*/ |
||||
|
setComponentRef(el: Element, fieldName: string) { |
||||
|
if (el) { |
||||
|
this.componentRef[fieldName] = el; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取字段样式 |
||||
|
* @param field 字段配置 |
||||
|
* @returns |
||||
|
*/ |
||||
|
getFieldStyle(field: any) { |
||||
|
const style = {}; |
||||
|
if (field) { |
||||
|
const screenColsNum = this.cm?.screenColsNumComputed.value; |
||||
|
if (field.firstCol) { |
||||
|
style['grid-column-start'] = 1; |
||||
|
if (field.colSpan === 'full' || (typeof field.colSpan === 'number' && screenColsNum < field.colSpan)) { |
||||
|
style['grid-column-end'] = `${screenColsNum + 1}`; |
||||
|
} else if (typeof field.colSpan === 'number' && field.colSpan > 0) { |
||||
|
style['grid-column-end'] = `${field.colSpan + 1}`; |
||||
|
} |
||||
|
} else { |
||||
|
if (field.colSpan === 'full') { |
||||
|
// col-span-${screenColsNumComputed.value}
|
||||
|
style['grid-column'] = `span ${screenColsNum} / span ${screenColsNum}`; |
||||
|
} else { |
||||
|
if (field.colSpan && screenColsNum >= field.colSpan) { |
||||
|
// col-span-${field.colSpan}
|
||||
|
style['grid-column'] = `span ${field.colSpan} / span ${field.colSpan}`; |
||||
|
} else { |
||||
|
// col-span-1
|
||||
|
style['grid-column'] = 'span 1 / span 1'; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return style; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据配置属性中的字段数组构建 Base 类中的 fields 与 fieldArray |
||||
|
*/ |
||||
|
buildFields() { |
||||
|
this.fieldArray.value = []; |
||||
|
this.fields = reactive({}); |
||||
|
this.fieldsHandler(this.props.fields, true); |
||||
|
} |
||||
|
/** |
||||
|
* 根据配置属性中的字段数组构建 Base 类中的 fields 与 fieldArray 并重新初始化数据模型 |
||||
|
*/ |
||||
|
buildFieldsAndData() { |
||||
|
this.buildFields(); |
||||
|
this.buildData(); |
||||
|
} |
||||
|
/** |
||||
|
* 字段处理 |
||||
|
* @param arr |
||||
|
* @param isRoot |
||||
|
*/ |
||||
|
private fieldsHandler(arr: Array<any>, isRoot: boolean) { |
||||
|
arr.forEach((field: any) => { |
||||
|
if (isRoot) { |
||||
|
this.fieldArray.value.push({ ...field }); |
||||
|
} |
||||
|
if (field.type === Constant.GROUP_TYPE && field.fields) { |
||||
|
this.fieldsHandler(field.fields, false); |
||||
|
} else if (Tools.hasOwnProperty(field, 'name')) { |
||||
|
this.fields[field.name] = { ...field }; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 构建表单初始数据对象 |
||||
|
*/ |
||||
|
private buildData() { |
||||
|
if (!Tools.isEmpty(this.props.modelValue)) { |
||||
|
// 用户进行了双向绑定,数据以 modelValue 为准。
|
||||
|
this.data = reactive(this.props.modelValue); |
||||
|
} else { |
||||
|
// 根据 fields 构建表单初始数据对象
|
||||
|
const modelValue = this.buildModelValueByFields(); |
||||
|
this.data = reactive(modelValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据当前字段构建模型值对象 |
||||
|
* @returns |
||||
|
*/ |
||||
|
buildModelValueByFields() { |
||||
|
const modelValue = {}; |
||||
|
Object.keys(this.fields).forEach((fieldName: string) => { |
||||
|
modelValue[fieldName] = getDefaultValue(this.fields[fieldName]); |
||||
|
}); |
||||
|
return modelValue; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据字段名在字段集合中查找字段 |
||||
|
* @param fieldName |
||||
|
* @returns |
||||
|
*/ |
||||
|
fieldArrayFindByName(fieldName: string) { |
||||
|
return this.recursiveGetField(this.fieldArray.value, fieldName); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据字段名在字段集合中递归获取 |
||||
|
* @param arr 集合 |
||||
|
* @param fieldName 字段名称 |
||||
|
* @returns |
||||
|
*/ |
||||
|
private recursiveGetField(arr: Array<any>, fieldName: string) { |
||||
|
let result: any = undefined; |
||||
|
arr.forEach((field: any) => { |
||||
|
if (field.type === Constant.GROUP_TYPE && field.fields) { |
||||
|
const field_ = this.recursiveGetField(field.fields, fieldName); |
||||
|
if (field_) { |
||||
|
result = field_; |
||||
|
} |
||||
|
} else if (Tools.hasOwnProperty(field, 'name') && field.name === fieldName) { |
||||
|
result = field; |
||||
|
} |
||||
|
}); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 字段集合递归处理 |
||||
|
* @param arr 集合 |
||||
|
* @param fieldName 若传递只处理该字段 |
||||
|
* @param callBack 回调函数 |
||||
|
* @returns |
||||
|
*/ |
||||
|
fieldArrayRecursiveHandler(arr: Array<any>, callBack: any, fieldName?: string) { |
||||
|
arr.forEach((field: any) => { |
||||
|
if (field.type === Constant.GROUP_TYPE && field.fields) { |
||||
|
this.fieldArrayRecursiveHandler(field.fields, callBack, fieldName); |
||||
|
} else if (Tools.hasOwnProperty(field, 'name')) { |
||||
|
if (Tools.isEmpty(fieldName)) { |
||||
|
callBack(field); |
||||
|
} else if (field.name === fieldName) { |
||||
|
callBack(field); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
export type ComponentFunctionsType = { |
||||
|
/** |
||||
|
* 获取表单ref函数 |
||||
|
*/ |
||||
|
getFormRef: Function; |
||||
|
/** |
||||
|
* 表单字段模型值更改触发函数 |
||||
|
*/ |
||||
|
updateModelValue: Function; |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
export type ErrorType = { |
||||
|
fieldName: string; |
||||
|
errorMessage: string; |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
export type PropsType = { |
||||
|
/** |
||||
|
* form模型值 |
||||
|
*/ |
||||
|
modelValue?: any, |
||||
|
/** |
||||
|
* 一行显示的字段数量,为0时根据屏幕断点自动显示 |
||||
|
*/ |
||||
|
colsNum?: any, |
||||
|
/** |
||||
|
* 横向X轴两个元素间的空隙像素点 |
||||
|
*/ |
||||
|
xGap?: number, |
||||
|
/** |
||||
|
* 纵向Y轴两个元素间的空隙像素点 |
||||
|
*/ |
||||
|
yGap?: number, |
||||
|
/** |
||||
|
* 表单字段集合 |
||||
|
*/ |
||||
|
fields: Array<any>, |
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
<template> |
||||
|
<q-item :clickable="true" @click="click"> |
||||
|
<q-item-section> |
||||
|
<q-item-label>斑马纹</q-item-label> |
||||
|
</q-item-section> |
||||
|
<q-item-section side> |
||||
|
<q-btn-group outline flat dense unelevated spread> |
||||
|
<q-btn |
||||
|
:color="tools.table.configStore.stripe ? 'primary' : ''" |
||||
|
:outline="tools.table.configStore.stripe ? false : true" |
||||
|
dense |
||||
|
label="显示" |
||||
|
unelevated |
||||
|
/> |
||||
|
<q-btn |
||||
|
:color="tools.table.configStore.stripe ? '' : 'primary'" |
||||
|
:outline="tools.table.configStore.stripe ? true : false" |
||||
|
dense |
||||
|
label="隐藏" |
||||
|
unelevated |
||||
|
/> |
||||
|
</q-btn-group> |
||||
|
</q-item-section> |
||||
|
</q-item> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { inject } from 'vue'; |
||||
|
import { GridTools } from '../../../ts/index'; |
||||
|
|
||||
|
const tools = <GridTools>inject('tools'); |
||||
|
const click = () => { |
||||
|
tools.table.configStore.stripe = !tools.table.configStore.stripe; |
||||
|
if (tools.table.configStore.stripe && !tools.opFM.stripeWatch) { |
||||
|
tools.opFM.setStripe(); |
||||
|
} else { |
||||
|
tools.opFM.resetStripeStyle(); |
||||
|
} |
||||
|
}; |
||||
|
</script> |
@ -1,54 +1,227 @@ |
|||||
<template> |
<template> |
||||
<w-grid |
<q-splitter :model-value="60" class="w-full" style="height: 100%"> |
||||
title="用户列表" |
<template #before> |
||||
:checkbox-selection="false" |
<div class="pr-1" style="height: 100%"> |
||||
:data-url="Environment.apiContextPath('/api/system/user')" |
<w-grid |
||||
:pageable="false" |
ref="roleGridRef" |
||||
:toolbar-actions="[ |
:title="$t('system.role.grid.title')" |
||||
'query', |
:config-button="true" |
||||
'reset', |
selection="multiple" |
||||
'separator', |
db-click-operation="edit" |
||||
'add', |
:checkbox-selection="true" |
||||
'edit', |
:data-url="Environment.apiContextPath('/api/system/role')" |
||||
'remove', |
:pagination="{ |
||||
'expand', |
sortBy: 'name', |
||||
[ |
descending: false, |
||||
{ |
}" |
||||
name: 'ddd', |
:query-form-cols-num="3" |
||||
icon: 'add', |
:query-form-fields="[ |
||||
label: $t('loginName'), |
{ name: 'code', label: $t('code'), type: 'w-text' }, |
||||
}, |
{ name: 'name', label: $t('name'), type: 'w-text' }, |
||||
'addTop', |
{ name: 'enable', label: $t('isEnable'), type: 'w-select', options: Options.yesNo() }, |
||||
['addChild', 'cellEdit', 'clone'], |
]" |
||||
], |
:toolbar-configure="{ noIcon: false }" |
||||
]" |
:toolbar-actions="['query', 'refresh', 'separator', 'add', 'clone', 'edit', 'remove', 'separator', 'view', 'separator', 'export']" |
||||
:advanced-query="true" |
:columns="[ |
||||
:stripe="true" |
{ width: 200, name: 'code', label: $t('code') }, |
||||
:query-form-fields="[ |
{ width: '100%', name: 'name', label: $t('name') }, |
||||
{ name: 'defaultOrgId', label: '所属机构', type: 'w-org-select', multiple: true }, |
{ width: 70, name: 'enable', label: $t('status'), align: 'center', format: Formater.enableTag() }, |
||||
{ name: 'lastModifyDate', label: $t('lastModifyDate'), type: 'w-date-range' }, |
{ |
||||
{ name: 'loginName', label: $t('loginName'), type: 'w-text', showIf: true }, |
width: 200, |
||||
{ name: 'userName', label: $t('userName'), type: 'w-text' }, |
name: 'corporationCode', |
||||
]" |
label: $t('corporation'), |
||||
:columns="[ |
showIf: SessionManager.isPrimaryCorporation(), |
||||
{ name: 'loginName', label: '很长很长的表头可绕地球一圈', width: 100, title: 'dfdf' }, |
format: (value) => { |
||||
{ name: 'userName', label: $t('userName') }, |
return corporationMapRef[value]; |
||||
{ name: 'lastModifier', label: '最后修改人' }, |
}, |
||||
{ |
}, |
||||
name: 'lastModifyDate', |
]" |
||||
label: $t('lastModifyDate'), |
:editor="{ |
||||
}, |
dialog: { |
||||
]" |
width: '600px', |
||||
:editor="{ |
}, |
||||
form: { |
form: { |
||||
fields: [ |
colsNum: 1, |
||||
{ name: 'loginName', label: $t('loginName'), type: 'w-text' }, |
fields: [ |
||||
{ name: 'userName', label: $t('userName'), type: 'w-text' }, |
{ name: 'code', label: $t('code'), type: 'w-text', requiredIf: true }, |
||||
], |
{ name: 'name', label: $t('name'), type: 'w-text', requiredIf: true }, |
||||
}, |
{ name: 'description', label: $t('description'), type: 'w-textarea', rows: 1 }, |
||||
}" |
{ |
||||
></w-grid> |
name: 'corporationCode', |
||||
|
label: $t('corporation'), |
||||
|
type: 'w-select', |
||||
|
options: corporationOptionRef, |
||||
|
showIf: () => { |
||||
|
return SessionManager.isPrimaryCorporation(); |
||||
|
}, |
||||
|
}, |
||||
|
{ name: 'enable', label: $t('enable'), type: 'w-checkbox', defaultValue: true }, |
||||
|
], |
||||
|
}, |
||||
|
}" |
||||
|
:viewer="{ |
||||
|
panel: { |
||||
|
columnNum: 1, |
||||
|
fields: [ |
||||
|
{ name: 'id', label: $t('id') }, |
||||
|
{ name: 'code', label: $t('code') }, |
||||
|
{ name: 'name', label: $t('name') }, |
||||
|
{ name: 'description', label: $t('description') }, |
||||
|
{ name: 'enable', label: $t('enable'), format: Formater.none() }, |
||||
|
{ name: 'dataComeFrom', label: $t('dataComeFrom') }, |
||||
|
{ name: 'creator', label: $t('creator') }, |
||||
|
{ name: 'createDate', label: $t('createDate') }, |
||||
|
{ name: 'lastModifier', label: $t('lastModifier') }, |
||||
|
{ name: 'lastModifyDate', label: $t('lastModifyDate'), format: Formater.none() }, |
||||
|
{ name: 'corporationCode', label: $t('corporationCode') }, |
||||
|
], |
||||
|
}, |
||||
|
}" |
||||
|
@row-click=" |
||||
|
(args) => { |
||||
|
refreshRelationshipComponents(args.row.id); |
||||
|
} |
||||
|
" |
||||
|
@before-request-data=" |
||||
|
() => { |
||||
|
menuTreeGridRef?.clear(); |
||||
|
userGridRef?.clear(); |
||||
|
} |
||||
|
" |
||||
|
> |
||||
|
</w-grid> |
||||
|
</div> |
||||
|
</template> |
||||
|
<template #after> |
||||
|
<div class="pl-1" style="height: 100%"> |
||||
|
<q-tabs v-model="selectedTabRef" inline-label align="left" :breakpoint="0" no-caps> |
||||
|
<q-tab name="menu" icon="bi-menu-app" :label="$t('menu')" /> |
||||
|
<q-tab name="user" icon="bi-person" :label="$t('user')" /> |
||||
|
</q-tabs> |
||||
|
|
||||
|
<q-tab-panels v-model="selectedTabRef" animated swipeable keep-alive style="height: calc(100% - 48px)"> |
||||
|
<q-tab-panel name="menu" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px"> |
||||
|
<SelectMenuTreeGrid |
||||
|
ref="menuTreeGridRef" |
||||
|
:fetch-data-url="Environment.apiContextPath('/api/system/menu/listAllMenusWithSelectedStatusByRole')" |
||||
|
foreign-key="roleId" |
||||
|
:foreign-value="currentSelectedRoleId" |
||||
|
@update="update" |
||||
|
></SelectMenuTreeGrid> |
||||
|
</q-tab-panel> |
||||
|
|
||||
|
<q-tab-panel name="user" class="px-0 pb-0" style="height: 100%; padding-left: 0px; padding-right: 0px; padding-bottom: 0px"> |
||||
|
<SelectUserGrid |
||||
|
ref="userGridRef" |
||||
|
:fetch-data-url="Environment.apiContextPath('/api/system/user/queryUsersByRole')" |
||||
|
:fetch-other-data-url="Environment.apiContextPath('/api/system/user/queryOtherUsersByRole')" |
||||
|
foreign-key="roleId" |
||||
|
:foreign-value="currentSelectedRoleId" |
||||
|
@select-in="selectIn" |
||||
|
@select-out="selectOut" |
||||
|
@select-all-in="selectAllIn" |
||||
|
@select-all-out="selectAllOut" |
||||
|
> |
||||
|
</SelectUserGrid> |
||||
|
</q-tab-panel> |
||||
|
</q-tab-panels> |
||||
|
</div> |
||||
|
</template> |
||||
|
</q-splitter> |
||||
</template> |
</template> |
||||
<script setup lang="ts"> |
<script setup lang="ts"> |
||||
import { Environment, Formater, $t } from '@/platform'; |
import { ref, onActivated } from 'vue'; |
||||
|
import { Environment, axios, Options, Formater, SessionManager } from '@/platform'; |
||||
|
import SelectUserGrid from './SelectUserGrid.vue'; |
||||
|
import SelectMenuTreeGrid from './SelectMenuTreeGrid.vue'; |
||||
|
|
||||
|
const corporationMapRef = ref({}); |
||||
|
const corporationOptionRef = ref([]); |
||||
|
const roleGridRef = ref(); |
||||
|
const userGridRef = ref(); |
||||
|
const menuTreeGridRef = ref(); |
||||
|
|
||||
|
const selectedTabRef = ref('menu'); |
||||
|
const currentSelectedRoleId = ref(''); |
||||
|
|
||||
|
const refreshRelationshipComponents = (id) => { |
||||
|
currentSelectedRoleId.value = id; |
||||
|
menuTreeGridRef.value?.refresh(); |
||||
|
userGridRef.value?.refresh(); |
||||
|
}; |
||||
|
|
||||
|
const selectIn = (ids: string[], gridComponent, dialogComponent) => { |
||||
|
axios |
||||
|
.post(Environment.apiContextPath('/api/system/role/addUsers'), { |
||||
|
one: roleGridRef.value.getSelectedRows()[0].id, |
||||
|
many: ids, |
||||
|
}) |
||||
|
.then(() => { |
||||
|
gridComponent?.refresh(); |
||||
|
dialogComponent?.close(); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const selectOut = (ids, gridComponent) => { |
||||
|
axios |
||||
|
.post(Environment.apiContextPath('/api/system/role/removeUsers'), { |
||||
|
one: roleGridRef.value.getSelectedRows()[0].id, |
||||
|
many: ids, |
||||
|
}) |
||||
|
.then(() => { |
||||
|
gridComponent?.refresh(); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const selectAllIn = (gridComponent) => { |
||||
|
axios |
||||
|
.post(Environment.apiContextPath('/api/system/role/addAllUsers'), { |
||||
|
one: roleGridRef.value.getSelectedRows()[0].id, |
||||
|
many: [], |
||||
|
}) |
||||
|
.then(() => { |
||||
|
gridComponent?.refresh(); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const selectAllOut = (gridComponent) => { |
||||
|
axios |
||||
|
.post(Environment.apiContextPath('/api/system/role/removeAllUsers'), { |
||||
|
one: roleGridRef.value.getSelectedRows()[0].id, |
||||
|
many: [], |
||||
|
}) |
||||
|
.then(() => { |
||||
|
gridComponent?.refresh(); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const update = (ids, gridComponent) => { |
||||
|
axios |
||||
|
.post(Environment.apiContextPath('/api/system/role/updateMenus'), { |
||||
|
one: roleGridRef.value.getSelectedRows()[0].id, |
||||
|
many: ids, |
||||
|
}) |
||||
|
.then(() => { |
||||
|
gridComponent.refresh(); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
onActivated(() => { |
||||
|
menuTreeGridRef.value?.refresh(); |
||||
|
userGridRef.value?.refresh(); |
||||
|
if (SessionManager.isPrimaryCorporation()) { |
||||
|
axios.get(Environment.apiContextPath('/api/system/corporation?pageable=false')).then((response) => { |
||||
|
const options = []; |
||||
|
const corporationMap = {}; |
||||
|
if (response.data?.content && response.data?.content.length > 0) { |
||||
|
for (const item of response.data.content) { |
||||
|
options.push({ label: item.name, value: item.code }); |
||||
|
corporationMap[item.code] = item.name; |
||||
|
} |
||||
|
} |
||||
|
corporationOptionRef.value = options; |
||||
|
corporationMapRef.value = corporationMap; |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
</script> |
</script> |
||||
|
@ -0,0 +1,125 @@ |
|||||
|
<template> |
||||
|
<w-grid |
||||
|
ref="treeGridRef" |
||||
|
:title="$t('system.shared.selectMenu.grid.title')" |
||||
|
hide-bottom |
||||
|
:config-button="false" |
||||
|
:tree="true" |
||||
|
selection="multiple" |
||||
|
:checkbox-selection="true" |
||||
|
tree-tick-strategy="strict" |
||||
|
:tree-icon=" |
||||
|
(row) => { |
||||
|
if (row.type === 'SEPARATOR') { |
||||
|
return { name: 'bi-dash-lg' }; |
||||
|
} else if (row.type === 'ROUTE_ACTION') { |
||||
|
return { name: 'sym_o_crop_16_9' }; |
||||
|
} else { |
||||
|
return { name: row.icon }; |
||||
|
} |
||||
|
} |
||||
|
" |
||||
|
:fetch-data-url="fetchDataUrl + '?' + foreignKey + '=' + foreignValue" |
||||
|
:auto-fetch-data="true" |
||||
|
:pageable="false" |
||||
|
:toolbar-configure="{ noIcon: false }" |
||||
|
:toolbar-actions="[ |
||||
|
'refresh', |
||||
|
'separator', |
||||
|
{ |
||||
|
extend: 'expand', |
||||
|
enableIf: () => { |
||||
|
return foreignValue && treeGridRef?.getRows()?.length > 0; |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
name: 'save', |
||||
|
label: $t('save'), |
||||
|
icon: 'bi-floppy', |
||||
|
enableIf: () => { |
||||
|
return foreignValue && treeGridRef?.getRows()?.length > 0; |
||||
|
}, |
||||
|
click: (arg) => { |
||||
|
DialogManager.confirm($t('system.shared.selectMenu.grid.toolbar.save.tip'), () => { |
||||
|
const ids = Tools.extractProperties(treeGridRef.getTickedRows(true), 'id'); |
||||
|
emit('update', ids, treeGridRef); |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
'separator', |
||||
|
'view', |
||||
|
]" |
||||
|
:columns="[ |
||||
|
{ |
||||
|
width: '100%', |
||||
|
name: 'titleI18nKey', |
||||
|
label: $t('name'), |
||||
|
sortable: false, |
||||
|
format: (value, row) => { |
||||
|
if (row.type === 'SEPARATOR') { |
||||
|
return `<hr style='width:100px'/>`; |
||||
|
} else if (row.type === 'ROUTE_ACTION') { |
||||
|
return $t(row.i18nKey); |
||||
|
} else { |
||||
|
return $t(value); |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
{ width: 70, name: 'enable', label: $t('status'), format: Formater.enableTag(), sortable: false }, |
||||
|
]" |
||||
|
:viewer="{ |
||||
|
panel: { |
||||
|
columnNum: 1, |
||||
|
fields: [ |
||||
|
{ name: 'id', label: $t('id') }, |
||||
|
{ name: 'type', label: $t('type') }, |
||||
|
{ name: 'name', label: $t('name') }, |
||||
|
{ name: 'titleI18nKey', label: $t('titleI18nKey') }, |
||||
|
{ name: 'icon', label: $t('icon') }, |
||||
|
{ name: 'enable', label: $t('enable') }, |
||||
|
{ name: 'order', label: $t('order') }, |
||||
|
{ name: 'javaScript', label: $t('javaScript') }, |
||||
|
{ name: 'url', label: $t('url') }, |
||||
|
{ name: 'urlOpenType', label: $t('urlOpenType') }, |
||||
|
{ name: 'routeName', label: $t('routeName') }, |
||||
|
{ name: 'routeQuery', label: $t('routeQuery') }, |
||||
|
{ name: 'dataComeFrom', label: $t('dataComeFrom') }, |
||||
|
{ name: 'creator', label: $t('creator') }, |
||||
|
{ name: 'createDate', label: $t('createDate') }, |
||||
|
{ name: 'lastModifier', label: $t('lastModifier') }, |
||||
|
{ name: 'lastModifyDate', label: $t('lastModifyDate') }, |
||||
|
{ name: 'corporationCode', label: $t('corporationCode') }, |
||||
|
], |
||||
|
}, |
||||
|
}" |
||||
|
></w-grid> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { ref } from 'vue'; |
||||
|
import { DialogManager, Formater, Tools } from '@/platform'; |
||||
|
|
||||
|
defineProps({ |
||||
|
fetchDataUrl: { type: String, default: '' }, |
||||
|
foreignKey: { type: String, default: '' }, |
||||
|
foreignValue: { type: String, default: '' }, |
||||
|
}); |
||||
|
|
||||
|
const emit = defineEmits<{ |
||||
|
(e: 'update', ids: string[], gridComponent: any): void; |
||||
|
}>(); |
||||
|
|
||||
|
const treeGridRef = ref(); |
||||
|
|
||||
|
const refresh = () => { |
||||
|
treeGridRef.value?.refresh(); |
||||
|
}; |
||||
|
|
||||
|
const clear = () => { |
||||
|
treeGridRef.value?.setLocalData([]); |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
refresh, |
||||
|
clear, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,165 @@ |
|||||
|
<template> |
||||
|
<w-grid |
||||
|
ref="gridRef" |
||||
|
:title="$t('system.shared.selectUser.grid.title')" |
||||
|
:config-button="false" |
||||
|
selection="multiple" |
||||
|
:checkbox-selection="true" |
||||
|
:fetch-data-url="fetchDataUrl + '?' + foreignKey + '=' + foreignValue" |
||||
|
:auto-fetch-data="true" |
||||
|
:toolbar-configure="{ noIcon: false }" |
||||
|
:toolbar-actions="[ |
||||
|
'refresh', |
||||
|
'separator', |
||||
|
{ |
||||
|
name: 'selectIn', |
||||
|
label: $t('system.shared.selectUser.grid.toolbar.selectIn'), |
||||
|
icon: 'bi-download', |
||||
|
enableIf: () => { |
||||
|
return foreignValue ? true : false; |
||||
|
}, |
||||
|
click: () => { |
||||
|
dialogRef.open(foreignValue); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
name: 'selectOut', |
||||
|
label: $t('system.shared.selectUser.grid.toolbar.selectOut'), |
||||
|
icon: 'bi-upload', |
||||
|
enableIf: () => { |
||||
|
return foreignValue && gridRef?.getSelectedRows()?.length > 0; |
||||
|
}, |
||||
|
click: (arg) => { |
||||
|
const ids = Tools.extractProperties(arg.selecteds, 'id'); |
||||
|
DialogManager.confirm($t('system.shared.selectUser.grid.toolbar.selectOut.tip'), () => { |
||||
|
emit('selectOut', ids, gridRef); |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
'separator', |
||||
|
{ |
||||
|
name: 'selectAllIn', |
||||
|
label: $t('system.shared.selectUser.grid.toolbar.selectAllIn'), |
||||
|
icon: 'bi-box-arrow-in-down', |
||||
|
enableIf: () => { |
||||
|
return foreignValue ? true : false; |
||||
|
}, |
||||
|
click: () => { |
||||
|
DialogManager.confirm($t('system.shared.selectUser.grid.toolbar.selectAllIn.tip'), () => { |
||||
|
emit('selectAllIn', gridRef); |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
name: 'selectAllOut', |
||||
|
label: $t('system.shared.selectUser.grid.toolbar.selectAllOut'), |
||||
|
icon: 'bi-box-arrow-up', |
||||
|
enableIf: () => { |
||||
|
return foreignValue && gridRef?.getRows()?.length > 0; |
||||
|
}, |
||||
|
click: () => { |
||||
|
DialogManager.confirm($t('system.shared.selectUser.grid.toolbar.selectAllOut.tip'), () => { |
||||
|
emit('selectAllOut', gridRef); |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
'separator', |
||||
|
'view', |
||||
|
]" |
||||
|
:columns="[ |
||||
|
{ width: 150, name: 'loginName', label: $t('loginName') }, |
||||
|
{ width: '100%', name: 'userName', label: $t('userName') }, |
||||
|
{ |
||||
|
width: 150, |
||||
|
name: 'enable', |
||||
|
label: $t('status'), |
||||
|
format: (value, row) => { |
||||
|
return { |
||||
|
componentType: UserStatusTag, |
||||
|
attrs: row, |
||||
|
}; |
||||
|
}, |
||||
|
}, |
||||
|
]" |
||||
|
:viewer="{ |
||||
|
panel: { |
||||
|
columnNum: 1, |
||||
|
fields: [ |
||||
|
{ name: 'id', label: $t('id') }, |
||||
|
{ name: 'loginName', label: $t('loginName') }, |
||||
|
{ name: 'userName', label: $t('userName') }, |
||||
|
{ name: 'description', label: $t('description') }, |
||||
|
{ |
||||
|
name: 'enable', |
||||
|
label: $t('enable'), |
||||
|
format: (value) => { |
||||
|
return value; |
||||
|
}, |
||||
|
}, |
||||
|
{ name: 'accountExpired', label: $t('accountExpired') }, |
||||
|
{ name: 'accountLocked', label: $t('accountLocked') }, |
||||
|
{ name: 'credentialsExpired', label: $t('credentialsExpired') }, |
||||
|
{ name: 'email', label: $t('email') }, |
||||
|
{ name: 'phone', label: $t('phone') }, |
||||
|
{ name: 'mobile', label: $t('mobile') }, |
||||
|
{ name: 'weixin', label: $t('weixin') }, |
||||
|
{ name: 'qq', label: $t('qq') }, |
||||
|
{ name: 'dataComeFrom', label: $t('dataComeFrom') }, |
||||
|
{ name: 'creator', label: $t('creator') }, |
||||
|
{ name: 'createDate', label: $t('createDate') }, |
||||
|
{ name: 'lastModifier', label: $t('lastModifier') }, |
||||
|
{ name: 'lastModifyDate', label: $t('lastModifyDate') }, |
||||
|
{ name: 'corporationCode', label: $t('corporationCode') }, |
||||
|
], |
||||
|
}, |
||||
|
}" |
||||
|
></w-grid> |
||||
|
<SelectUserDialog |
||||
|
ref="dialogRef" |
||||
|
:opener="gridRef" |
||||
|
:fetch-data-url="fetchOtherDataUrl" |
||||
|
:foreign-key="foreignKey" |
||||
|
:foreign-value="foreignValue" |
||||
|
@after-selected=" |
||||
|
(ids: string[]) => { |
||||
|
emit('selectIn', ids, gridRef, dialogRef); |
||||
|
} |
||||
|
" |
||||
|
></SelectUserDialog> |
||||
|
</template> |
||||
|
<script setup lang="ts"> |
||||
|
import { ref } from 'vue'; |
||||
|
import { DialogManager, Tools } from '@/platform'; |
||||
|
import SelectUserDialog from './SelectUserDialog.vue'; |
||||
|
import UserStatusTag from './UserStatusTag.vue'; |
||||
|
|
||||
|
defineProps({ |
||||
|
fetchDataUrl: { type: String, default: '' }, |
||||
|
fetchOtherDataUrl: { type: String, default: '' }, |
||||
|
foreignKey: { type: String, default: '' }, |
||||
|
foreignValue: { type: String, default: '' }, |
||||
|
}); |
||||
|
|
||||
|
const emit = defineEmits<{ |
||||
|
(e: 'selectIn', ids: string[], gridComponent: any, dialogComponent: any): void; |
||||
|
(e: 'selectOut', ids: string[], gridComponent: any): void; |
||||
|
(e: 'selectAllIn', gridComponent: any): void; |
||||
|
(e: 'selectAllOut', gridComponent: any): void; |
||||
|
}>(); |
||||
|
|
||||
|
const gridRef = ref(); |
||||
|
const dialogRef = ref(); |
||||
|
|
||||
|
const refresh = () => { |
||||
|
gridRef.value?.refresh(); |
||||
|
}; |
||||
|
|
||||
|
const clear = () => { |
||||
|
gridRef.value?.setLocalData([]); |
||||
|
}; |
||||
|
|
||||
|
defineExpose({ |
||||
|
refresh, |
||||
|
clear, |
||||
|
}); |
||||
|
</script> |
@ -0,0 +1,48 @@ |
|||||
|
package io.sc.platform.lcdp.frontend.component.support; |
||||
|
|
||||
|
import io.sc.platform.orm.service.support.OperatorType; |
||||
|
import io.sc.platform.orm.service.support.QueryParameter; |
||||
|
import io.sc.platform.orm.service.support.criteria.Criteria; |
||||
|
import io.sc.platform.orm.service.support.criteria.impl.InSet; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
public class CriteriaHandler { |
||||
|
|
||||
|
/** |
||||
|
* 根据 criteria条件集合与原始 QueryParameter 中的分页、页码等参数构建一个新的 QueryParameter |
||||
|
* @param queryParameter 原始QueryParameter |
||||
|
* @param criterias criteria条件集合 |
||||
|
* @return |
||||
|
*/ |
||||
|
public static QueryParameter buildQueryParameter(QueryParameter queryParameter, List<Criteria> criterias) { |
||||
|
QueryParameter newQueryParameter = new QueryParameter(); |
||||
|
newQueryParameter.setCriterias(criterias); |
||||
|
newQueryParameter.setSortBy(queryParameter.getSortBy()); |
||||
|
newQueryParameter.setPageable(queryParameter.getPageable()); |
||||
|
newQueryParameter.setPage(queryParameter.getPage()); |
||||
|
newQueryParameter.setOperator(queryParameter.getOperator()); |
||||
|
newQueryParameter.setSize(queryParameter.getSize()); |
||||
|
newQueryParameter.setExportFilename(queryParameter.getExportFilename()); |
||||
|
return newQueryParameter; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 给criteria集合增加 inset 操作 |
||||
|
* @param criteriaList 要增加的集合 |
||||
|
* @param fieldName inset操作字段名 |
||||
|
* @param value inset操作字段值 |
||||
|
*/ |
||||
|
public static void criteriaListAddInSet(List<Criteria> criteriaList, String fieldName, List<String> value) { |
||||
|
InSet inSet = new InSet(); |
||||
|
inSet.setFieldName(fieldName); |
||||
|
inSet.setOperator(OperatorType.inSet); |
||||
|
if (value!=null && value.size()>0) { |
||||
|
inSet.setValue(value.toArray(new String[0])); |
||||
|
} else { |
||||
|
// 如果传入的value为空,设置一个无法匹配到数据的数组
|
||||
|
inSet.setValue(new String[]{Long.toString(System.currentTimeMillis())}); |
||||
|
} |
||||
|
criteriaList.add(inSet); |
||||
|
} |
||||
|
} |
@ -1,46 +0,0 @@ |
|||||
package io.sc.platform.lcdp.frontend.component.support; |
|
||||
|
|
||||
import io.sc.platform.orm.service.support.QueryParameter; |
|
||||
|
|
||||
/** |
|
||||
* 前端-用户选择组件-用户查询请求入参 |
|
||||
*/ |
|
||||
public class UserSearchQueryParameter { |
|
||||
|
|
||||
/** |
|
||||
* 用户列表查询参数 |
|
||||
*/ |
|
||||
private QueryParameter queryParameter; |
|
||||
/** |
|
||||
* 机构查询参数 |
|
||||
*/ |
|
||||
private QueryParameter orgQueryParameter; |
|
||||
/** |
|
||||
* 角色查询参数 |
|
||||
*/ |
|
||||
private QueryParameter roleQueryParameter; |
|
||||
|
|
||||
public QueryParameter getQueryParameter() { |
|
||||
return queryParameter; |
|
||||
} |
|
||||
|
|
||||
public void setQueryParameter(QueryParameter queryParameter) { |
|
||||
this.queryParameter = queryParameter; |
|
||||
} |
|
||||
|
|
||||
public QueryParameter getOrgQueryParameter() { |
|
||||
return orgQueryParameter; |
|
||||
} |
|
||||
|
|
||||
public void setOrgQueryParameter(QueryParameter orgQueryParameter) { |
|
||||
this.orgQueryParameter = orgQueryParameter; |
|
||||
} |
|
||||
|
|
||||
public QueryParameter getRoleQueryParameter() { |
|
||||
return roleQueryParameter; |
|
||||
} |
|
||||
|
|
||||
public void setRoleQueryParameter(QueryParameter roleQueryParameter) { |
|
||||
this.roleQueryParameter = roleQueryParameter; |
|
||||
} |
|
||||
} |
|
Loading…
Reference in new issue