fix: 修复单选、多选、多行文本的只读样式

This commit is contained in:
gaoyunqi
2024-04-25 10:12:19 +08:00
parent 60ef2ca412
commit 64140ebbca
4 changed files with 381 additions and 373 deletions

View File

@ -146,6 +146,8 @@ export const basicComponents = [
typeName: t('编辑器'),
type: 'richtext-editor',
options: {
labelWidthMode: 'fix',
labelFixWidth: 120,
span: '',
defaultValue: '',
width: '100%',

View File

@ -2,202 +2,207 @@
* @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component
-->
<template>
<CheckboxGroup v-bind="attrs" v-model:value="checked" button-style="solid" @change="handleChange">
<template v-for="item in getOptions" :key="`${item.value}`">
<Checkbox :value="item.value" :disabled="item.disabled">
{{ item.label }}
</Checkbox>
</template>
</CheckboxGroup>
<div class="api-checkbox-group">
<CheckboxGroup v-show="!disabled" v-model:value="checked" button-style="solid" v-bind="attrs" @change="handleChange">
<template v-for="item in getOptions" :key="`${item.value}`">
<Checkbox :disabled="item.disabled" :value="item.value">
{{ item.label }}
</Checkbox>
</template>
</CheckboxGroup>
<div v-if="disabled" class="readonly-wrap">
{{ displayText() }}
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, computed, unref, watch, inject, watchEffect } from 'vue';
import { Checkbox } from 'ant-design-vue';
import { isFunction } from '/@/utils/is';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { propTypes } from '/@/utils/propTypes';
import { get, omit } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
import { getDicDetailList } from '/@/api/system/dic';
import { getDatasourceData } from '/@/api/system/datasource';
import { apiConfigFunc, camelCaseString, isValidJSON } from '/@/utils/event/design';
import { defineComponent, PropType, ref, computed, unref, watch, inject, watchEffect } from 'vue';
import { Checkbox } from 'ant-design-vue';
import { isFunction } from '/@/utils/is';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { propTypes } from '/@/utils/propTypes';
import { get, omit } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
import { getDicDetailList } from '/@/api/system/dic';
import { getDatasourceData } from '/@/api/system/datasource';
import { apiConfigFunc, camelCaseString, isValidJSON } from '/@/utils/event/design';
type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean };
type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean };
export default defineComponent({
name: 'ApiCheckboxGroup',
components: {
CheckboxGroup: Checkbox.Group,
Checkbox,
},
props: {
api: {
type: Function as PropType<(arg?: Recordable | string) => Promise<OptionsItem[]>>,
default: null,
},
params: {
type: [Object, String] as PropType<Recordable | string>,
default: () => ({}),
},
value: {
type: [Array, String],
},
isBtn: {
type: [Boolean] as PropType<boolean>,
default: false,
},
numberToString: propTypes.bool,
resultField: propTypes.string.def(''),
labelField: propTypes.string.def('label'),
valueField: propTypes.string.def('value'),
immediate: propTypes.bool.def(true),
//数据来源 默认为空 如果不为空 则参数 api
datasourceType: String,
//静态数据默认选择
// defaultValue: Array,
staticOptions: {
type: Array as PropType<OptionsItem[]>,
default: () => [],
},
apiConfig: {
type: Object,
default: () => {},
},
mainKey: String,
index: Number,
},
emits: ['options-change', 'change', 'update:value'],
setup(props, { emit }) {
const options = ref<OptionsItem[]>([]);
const loading = ref(false);
const attrs = useAttrs();
const { t } = useI18n();
const formModel = inject<any>('formModel', null);
const isCamelCase = inject<boolean>('isCamelCase', false);
// Embedded in the form, just use the hook binding to perform form verification
const checked = ref<string[] | any[]>([]);
export default defineComponent({
name: 'ApiCheckboxGroup',
components: {
CheckboxGroup: Checkbox.Group,
Checkbox
},
props: {
api: {
type: Function as PropType<(arg?: Recordable | string) => Promise<OptionsItem[]>>,
default: null
},
params: {
type: [Object, String] as PropType<Recordable | string>,
default: () => ({})
},
value: {
type: [Array, String]
},
isBtn: {
type: [Boolean] as PropType<boolean>,
default: false
},
numberToString: propTypes.bool,
resultField: propTypes.string.def(''),
labelField: propTypes.string.def('label'),
valueField: propTypes.string.def('value'),
immediate: propTypes.bool.def(true),
//数据来源 默认为空 如果不为空 则参数 api
datasourceType: String,
//静态数据默认选择
// defaultValue: Array,
staticOptions: {
type: Array as PropType<OptionsItem[]>,
default: () => []
},
apiConfig: {
type: Object,
default: () => {}
},
mainKey: String,
index: Number,
disabled: Boolean
},
emits: ['options-change', 'change', 'update:value'],
setup(props, { emit }) {
const options = ref<OptionsItem[]>([]);
const loading = ref(false);
const attrs = useAttrs();
const { t } = useI18n();
const formModel = inject<any>('formModel', null);
const isCamelCase = inject<boolean>('isCamelCase', false);
// Embedded in the form, just use the hook binding to perform form verification
const checked = ref<string[] | any[]>([]);
// Processing options value
const getOptions = computed(() => {
const { labelField, valueField, numberToString } = props;
return unref(options).reduce((prev, next: Recordable) => {
if (next) {
const value = next[valueField];
prev.push({
label: next[labelField],
value: numberToString ? `${value}` : value,
...omit(next, [labelField, valueField]),
// Processing options value
const getOptions = computed(() => {
const { labelField, valueField, numberToString } = props;
return unref(options).reduce((prev, next: Recordable) => {
if (next) {
const value = next[valueField];
prev.push({
label: next[labelField],
value: numberToString ? `${value}` : value,
...omit(next, [labelField, valueField])
});
}
return prev;
}, [] as OptionsItem[]);
});
}
return prev;
}, [] as OptionsItem[]);
});
watchEffect(() => {
if (props.datasourceType === 'api' && props.apiConfig?.apiParams) {
props.apiConfig.apiParams.forEach((params) => {
params.tableInfo?.forEach((o) => {
if (o.bindType == 'data') {
let val = isValidJSON(o.value);
let field = '';
if (val && val.bindTable) {
let table = !isCamelCase
? val.bindTable + 'List'
: camelCaseString(val.bindTable + '_List');
field = !isCamelCase ? val.bindField : camelCaseString(val.bindField);
formModel &&
formModel[table!][props.index || 0] &&
formModel[table!][props.index || 0][field];
} else if (val && val.bindField) {
field = !isCamelCase ? val.bindField : camelCaseString(val.bindField);
formModel && formModel[field];
watchEffect(() => {
if (props.datasourceType === 'api' && props.apiConfig?.apiParams) {
props.apiConfig.apiParams.forEach((params) => {
params.tableInfo?.forEach((o) => {
if (o.bindType == 'data') {
let val = isValidJSON(o.value);
let field = '';
if (val && val.bindTable) {
let table = !isCamelCase ? val.bindTable + 'List' : camelCaseString(val.bindTable + '_List');
field = !isCamelCase ? val.bindField : camelCaseString(val.bindField);
formModel && formModel[table!][props.index || 0] && formModel[table!][props.index || 0][field];
} else if (val && val.bindField) {
field = !isCamelCase ? val.bindField : camelCaseString(val.bindField);
formModel && formModel[field];
}
}
});
});
fetch();
}
}
});
});
fetch();
}
});
watch(
() => [props.datasourceType, props.params, props.apiConfig],
() => {
fetch();
},
{
immediate: true,
deep: true,
},
);
watch(
() => props.value,
(val: any) => {
checked.value = typeof val === 'string' ? val?.split(',') : val;
},
{
immediate: true,
},
);
async function fetch() {
let api;
if (props.datasourceType) {
if (props.datasourceType === 'dic') {
api = getDicDetailList;
}
if (props.datasourceType === 'datasource') {
api = getDatasourceData;
}
if (props.datasourceType === 'staticData') {
options.value = props.staticOptions;
}
if (props.datasourceType === 'api') {
options.value = await apiConfigFunc(
props.apiConfig,
isCamelCase,
formModel,
props.index,
watch(
() => [props.datasourceType, props.params, props.apiConfig],
() => {
fetch();
},
{
immediate: true,
deep: true
}
);
}
watch(
() => props.value,
(val: any) => {
checked.value = typeof val === 'string' ? val?.split(',') : val;
},
{
immediate: true
}
);
async function fetch() {
let api;
if (props.datasourceType) {
if (props.datasourceType === 'dic') {
api = getDicDetailList;
}
if (props.datasourceType === 'datasource') {
api = getDatasourceData;
}
if (props.datasourceType === 'staticData') {
options.value = props.staticOptions;
}
if (props.datasourceType === 'api') {
options.value = await apiConfigFunc(props.apiConfig, isCamelCase, formModel, props.index);
}
}
if (!api || !isFunction(api)) return;
options.value = [];
try {
if (!props.params) return;
loading.value = true;
const res = await api(props.params);
if (Array.isArray(res)) {
options.value = res;
emitChange();
return;
}
if (props.resultField) {
options.value = get(res, props.resultField) || [];
}
emitChange();
} catch (error) {
console.warn(error);
} finally {
loading.value = false;
}
}
function emitChange() {
emit('options-change', unref(getOptions));
}
function handleChange(value) {
emit('update:value', value.toString());
emit('change');
checked.value = props.value === undefined || props.value === null ? value : (props.value as any).split(',');
}
function displayText() {
const _checked = checked.value;;
const labelArr = [];
(getOptions.value || []).forEach((opt) => {
if (_checked.includes(opt.value)) {
labelArr.push(opt.label);
}
});
return labelArr.join(', ');
}
return { checked, getOptions, attrs, loading, t, handleChange, props, displayText };
}
if (!api || !isFunction(api)) return;
options.value = [];
try {
if (!props.params) return;
loading.value = true;
const res = await api(props.params);
if (Array.isArray(res)) {
options.value = res;
emitChange();
return;
}
if (props.resultField) {
options.value = get(res, props.resultField) || [];
}
emitChange();
} catch (error) {
console.warn(error);
} finally {
loading.value = false;
}
}
function emitChange() {
emit('options-change', unref(getOptions));
}
function handleChange(value) {
emit('update:value', value.toString());
emit('change');
checked.value =
props.value === undefined || props.value === null
? value
: (props.value as any).split(',');
}
return { checked, getOptions, attrs, loading, t, handleChange, props };
},
});
});
</script>

View File

@ -2,200 +2,201 @@
* @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component
-->
<template>
<RadioGroup
v-bind="attrs"
v-model:value="checked"
:optionType="optionType"
button-style="solid"
@change="handleChange"
>
<template v-for="item in getOptions" :key="`${item.value}`">
<RadioButton v-if="optionType === 'button'" :value="item.value" :disabled="item.disabled">
{{ item.label }}
</RadioButton>
<Radio v-else :value="item.value" :disabled="item.disabled">
{{ item.label }}
</Radio>
</template>
</RadioGroup>
<div class="api-radio-group">
<RadioGroup v-show="!disabled" v-model:value="checked" :optionType="optionType" button-style="solid" v-bind="attrs" @change="handleChange">
<template v-for="item in getOptions" :key="`${item.value}`">
<RadioButton v-if="optionType === 'button'" :disabled="item.disabled" :value="item.value">
{{ item.label }}
</RadioButton>
<Radio v-else :disabled="item.disabled" :value="item.value">
{{ item.label }}
</Radio>
</template>
</RadioGroup>
<div v-if="disabled">
{{ displayText() }}
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, computed, unref, watch, inject, watchEffect } from 'vue';
import { Radio } from 'ant-design-vue';
import { isFunction } from '/@/utils/is';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { propTypes } from '/@/utils/propTypes';
import { get, omit } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
import { getDicDetailList } from '/@/api/system/dic';
import { getDatasourceData } from '/@/api/system/datasource';
import { apiConfigFunc, camelCaseString, isValidJSON } from '/@/utils/event/design';
import { defineComponent, PropType, ref, computed, unref, watch, inject, watchEffect } from 'vue';
import { Radio } from 'ant-design-vue';
import { isFunction } from '/@/utils/is';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { propTypes } from '/@/utils/propTypes';
import { get, omit } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
import { getDicDetailList } from '/@/api/system/dic';
import { getDatasourceData } from '/@/api/system/datasource';
import { apiConfigFunc, camelCaseString, isValidJSON } from '/@/utils/event/design';
type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean };
type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean };
export default defineComponent({
name: 'ApiRadioGroup',
components: {
RadioGroup: Radio.Group,
RadioButton: Radio.Button,
Radio,
},
props: {
api: {
type: Function as PropType<(arg?: Recordable | string) => Promise<OptionsItem[]>>,
default: null,
},
params: {
type: [Object, String] as PropType<Recordable | string>,
default: () => ({}),
},
value: {
type: [String, Number, Boolean] as PropType<string | number | boolean>,
},
isBtn: {
type: [Boolean] as PropType<boolean>,
default: false,
},
numberToString: propTypes.bool,
resultField: propTypes.string.def(''),
labelField: propTypes.string.def('label'),
valueField: propTypes.string.def('value'),
immediate: propTypes.bool.def(true),
//数据来源 默认为空 如果不为空 则参数 api
datasourceType: String,
optionType: propTypes.string.def('default'),
staticOptions: {
type: Array as PropType<OptionsItem[]>,
default: () => [],
},
apiConfig: Object,
mainKey: String,
index: Number,
},
emits: ['options-change', 'change', 'update:value'],
setup(props, { emit }) {
const options = ref<OptionsItem[]>([]);
const loading = ref(false);
const attrs = useAttrs();
const { t } = useI18n();
const formModel = inject<any>('formModel', null);
const isCamelCase = inject<boolean>('isCamelCase', false);
// Embedded in the form, just use the hook binding to perform form verification
const checked = ref<string | number>('');
// Processing options value
const getOptions = computed(() => {
const { labelField, valueField, numberToString } = props;
return unref(options).reduce((prev, next: Recordable) => {
if (next) {
const value = next[valueField];
prev.push({
label: next[labelField],
value: numberToString ? `${value}` : value,
...omit(next, [labelField, valueField]),
export default defineComponent({
name: 'ApiRadioGroup',
components: {
RadioGroup: Radio.Group,
RadioButton: Radio.Button,
Radio
},
props: {
api: {
type: Function as PropType<(arg?: Recordable | string) => Promise<OptionsItem[]>>,
default: null
},
params: {
type: [Object, String] as PropType<Recordable | string>,
default: () => ({})
},
value: {
type: [String, Number, Boolean] as PropType<string | number | boolean>
},
isBtn: {
type: [Boolean] as PropType<boolean>,
default: false
},
numberToString: propTypes.bool,
resultField: propTypes.string.def(''),
labelField: propTypes.string.def('label'),
valueField: propTypes.string.def('value'),
immediate: propTypes.bool.def(true),
//数据来源 默认为空 如果不为空 则参数 api
datasourceType: String,
optionType: propTypes.string.def('default'),
staticOptions: {
type: Array as PropType<OptionsItem[]>,
default: () => []
},
apiConfig: Object,
mainKey: String,
index: Number
},
emits: ['options-change', 'change', 'update:value'],
setup(props, { emit }) {
const options = ref<OptionsItem[]>([]);
const loading = ref(false);
const attrs = useAttrs();
const { t } = useI18n();
const formModel = inject<any>('formModel', null);
const isCamelCase = inject<boolean>('isCamelCase', false);
// Embedded in the form, just use the hook binding to perform form verification
const checked = ref<string | number>('');
const disabled = attrs.value.disabled;
// Processing options value
const getOptions = computed(() => {
const { labelField, valueField, numberToString } = props;
return unref(options).reduce((prev, next: Recordable) => {
if (next) {
const value = next[valueField];
prev.push({
label: next[labelField],
value: numberToString ? `${value}` : value,
...omit(next, [labelField, valueField])
});
}
return prev;
}, [] as OptionsItem[]);
});
}
return prev;
}, [] as OptionsItem[]);
});
watchEffect(() => {
if (props.datasourceType === 'api' && props.apiConfig?.apiParams) {
props.apiConfig.apiParams.forEach((params) => {
params.tableInfo?.forEach((o) => {
if (o.bindType == 'data') {
let val = isValidJSON(o.value);
let field = '';
if (val && val.bindTable) {
let table = !isCamelCase
? val.bindTable + 'List'
: camelCaseString(val.bindTable + '_List');
field = !isCamelCase ? val.bindField : camelCaseString(val.bindField);
watchEffect(() => {
if (props.datasourceType === 'api' && props.apiConfig?.apiParams) {
props.apiConfig.apiParams.forEach((params) => {
params.tableInfo?.forEach((o) => {
if (o.bindType == 'data') {
let val = isValidJSON(o.value);
let field = '';
if (val && val.bindTable) {
let table = !isCamelCase ? val.bindTable + 'List' : camelCaseString(val.bindTable + '_List');
field = !isCamelCase ? val.bindField : camelCaseString(val.bindField);
formModel &&
formModel[table!][props.index || 0] &&
formModel[table!][props.index || 0][field];
} else if (val && val.bindField) {
field = !isCamelCase ? val.bindField : camelCaseString(val.bindField);
formModel && formModel[field];
formModel && formModel[table!][props.index || 0] && formModel[table!][props.index || 0][field];
} else if (val && val.bindField) {
field = !isCamelCase ? val.bindField : camelCaseString(val.bindField);
formModel && formModel[field];
}
}
});
});
fetch();
}
}
});
});
fetch();
}
});
watch(
() => [props.params, props.datasourceType, props.apiConfig],
() => {
fetch();
},
{ deep: true, immediate: true },
);
watch(
() => props.value,
(val: any) => {
checked.value = val;
},
{
immediate: true,
},
);
async function fetch() {
let api;
if (props.datasourceType) {
if (props.datasourceType === 'dic') {
api = getDicDetailList;
}
if (props.datasourceType === 'datasource') {
api = getDatasourceData;
}
if (props.datasourceType === 'staticData') {
options.value = props.staticOptions;
}
if (props.datasourceType === 'api') {
options.value = await apiConfigFunc(
props.apiConfig,
isCamelCase,
formModel,
props.index,
watch(
() => [props.params, props.datasourceType, props.apiConfig],
() => {
fetch();
},
{ deep: true, immediate: true }
);
}
} else {
api = props.api;
watch(
() => props.value,
(val: any) => {
checked.value = val;
},
{
immediate: true
}
);
async function fetch() {
let api;
if (props.datasourceType) {
if (props.datasourceType === 'dic') {
api = getDicDetailList;
}
if (props.datasourceType === 'datasource') {
api = getDatasourceData;
}
if (props.datasourceType === 'staticData') {
options.value = props.staticOptions;
}
if (props.datasourceType === 'api') {
options.value = await apiConfigFunc(props.apiConfig, isCamelCase, formModel, props.index);
}
} else {
api = props.api;
}
if (!api || !isFunction(api)) return;
options.value = [];
try {
if (!props.params) return;
loading.value = true;
const res = await api(props.params);
if (Array.isArray(res)) {
options.value = res;
emitChange();
return;
}
if (props.resultField) {
options.value = get(res, props.resultField) || [];
}
emitChange();
} catch (error) {
console.warn(error);
} finally {
loading.value = false;
}
}
function emitChange() {
emit('options-change', unref(getOptions));
}
function handleChange({ target }) {
emit('update:value', target.value);
emit('change');
}
function displayText() {
const val = checked.value;
const options = getOptions.value || [];
const chkItem = options.find((item) => item.value === val);
if (chkItem) {
return chkItem.label;
}
return '';
}
return { checked, getOptions, attrs, loading, t, handleChange, props, displayText, disabled };
}
if (!api || !isFunction(api)) return;
options.value = [];
try {
if (!props.params) return;
loading.value = true;
const res = await api(props.params);
if (Array.isArray(res)) {
options.value = res;
emitChange();
return;
}
if (props.resultField) {
options.value = get(res, props.resultField) || [];
}
emitChange();
} catch (error) {
console.warn(error);
} finally {
loading.value = false;
}
}
function emitChange() {
emit('options-change', unref(getOptions));
}
function handleChange({ target }) {
emit('update:value', target.value);
emit('change');
}
return { checked, getOptions, attrs, loading, t, handleChange, props };
},
});
});
</script>

View File

@ -417,7 +417,7 @@
}
function readonlySupport(name) {
return /^(Input|AutoCodeRule|DatePicker|Text|TimePicker|Range|RichTextEditor)$/.test(name);
return /^(Input|AutoCodeRule|DatePicker|Text|TimePicker|Range|RichTextEditor|TimeRangePicker|RangePicker|InputTextArea)$/.test(name);
}
function getShow(schema: FormSchema): boolean {