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

View File

@ -417,7 +417,7 @@
} }
function readonlySupport(name) { 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 { function getShow(schema: FormSchema): boolean {