diff --git a/package.json b/package.json index 2b8bce07..027a9681 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "moviepilot", - "version": "2.14.4", + "version": "2.14.5", "private": true, "type": "module", "bin": "dist/service.js", diff --git a/src/plugins/vuetify/AppInput.ts b/src/plugins/vuetify/AppInput.ts index fec9f136..49dcec51 100644 --- a/src/plugins/vuetify/AppInput.ts +++ b/src/plugins/vuetify/AppInput.ts @@ -24,6 +24,15 @@ function hasDisplayText(value: unknown): value is string | number { return (typeof value === 'string' && value.trim().length > 0) || typeof value === 'number' } +/** + * 判断录入控件模型中是否已有可展示的值。 + */ +function hasInputValue(value: unknown) { + if (Array.isArray(value)) return value.length > 0 + + return value !== undefined && value !== null && value !== '' +} + /** * 解析包装组件尚未声明的 Vue 布尔属性值。 */ @@ -127,12 +136,19 @@ export function createResponsiveInputAdapter(component: Component, options: Resp const { label: labelSlot, ...controlSlots } = slots const labelContent = labelSlot?.({ label, props: { for: controlId } }) ?? String(label) const disabled = isBooleanAttributeEnabled(attrs.disabled) + const isField = options.kind === 'field' || options.kind === 'multiline' + const isEmptyWithoutPlaceholder = isField + && !hasInputValue(attrs.modelValue ?? attrs['model-value']) + && !hasDisplayText(attrs.placeholder) return h('div', { class: [ 'app-responsive-input', `app-responsive-input--${options.kind}`, - { 'app-responsive-input--disabled': disabled }, + { + 'app-responsive-input--disabled': disabled, + 'app-responsive-input--empty': isEmptyWithoutPlaceholder, + }, rootClass, ], style: rootStyle, diff --git a/src/styles/common.scss b/src/styles/common.scss index e73bba45..08445433 100644 --- a/src/styles/common.scss +++ b/src/styles/common.scss @@ -566,6 +566,11 @@ html[data-theme-radius='extra'] { transition: background-color 0.18s ease, box-shadow 0.18s ease; } +.app-responsive-input--empty:not(:has(.v-field--dirty)) .v-field { + background-color: rgba(var(--v-theme-on-surface), 0.035); + box-shadow: inset 0 0 0 1px rgba(var(--v-theme-on-surface), 0.14); +} + .app-responsive-input--field .v-field__outline, .app-responsive-input--field .v-field__overlay, .app-responsive-input--multiline .v-field__outline, @@ -588,11 +593,13 @@ html[data-theme-radius='extra'] { } .app-responsive-input--multiline { + row-gap: 0.5rem; align-items: start; + grid-template-columns: minmax(0, 1fr); } .app-responsive-input--multiline .app-responsive-input__control { - padding-block-start: 0.125rem; + inline-size: 100%; } .app-responsive-input--multiline .v-field__input { @@ -631,6 +638,14 @@ html[data-theme-radius='extra'] { opacity: var(--v-disabled-opacity); } +// 进入移动录入模式的控件强制独占一行,避免原 VCol 小于 12 列时两栏布局被再次压缩。 +@media (width < 960px) { + .v-row > :is(.v-col, [class*='v-col-']):has(.app-responsive-input) { + flex: 0 0 100%; + max-width: 100%; + } +} + @media (hover: hover) { .app-responsive-input--field .v-field:hover, .app-responsive-input--multiline .v-field:hover {