diff --git a/docs/module-federation-guide.md b/docs/module-federation-guide.md index afc4d2a2..f9058a8e 100644 --- a/docs/module-federation-guide.md +++ b/docs/module-federation-guide.md @@ -50,7 +50,7 @@ export default defineConfig({ plugins: [ vue(), federation({ - name: 'MyPlugin', // 需与插件的ID保持一致 + name: 'MyPlugin', filename: 'remoteEntry.js', exposes: { './Page': './src/components/Page.vue', @@ -60,13 +60,18 @@ export default defineConfig({ shared: { vue: { requiredVersion: false, + generate: false, }, vuetify: { requiredVersion: false, + generate: false, + singleton: true, }, 'vuetify/styles': { requiredVersion: false, - } + generate: false, + singleton: true, + }, }, format: 'esm' }) @@ -74,14 +79,56 @@ export default defineConfig({ build: { target: 'esnext', // 必须设置为esnext以支持顶层await minify: false, // 开发阶段建议关闭混淆 - cssCodeSplit: false, + cssCodeSplit: true, // 改为true以便能分离样式文件 + rollupOptions: { + output: { + manualChunks: { + 'vuetify-lib': ['vuetify'] // 将vuetify单独分离出来 + } + } + } + }, + css: { + preprocessorOptions: { + scss: { + additionalData: '/* 覆盖vuetify样式 */', + } + }, + postcss: { + plugins: [ + { + postcssPlugin: 'internal:charset-removal', + AtRule: { + charset: (atRule) => { + if (atRule.name === 'charset') { + atRule.remove(); + } + } + } + }, + { + postcssPlugin: 'vuetify-filter', + Root(root) { + // 过滤掉所有vuetify相关的CSS + root.walkRules(rule => { + if (rule.selector && ( + rule.selector.includes('.v-') || + rule.selector.includes('.mdi-'))) { + rule.remove(); + } + }); + } + } + ] + } }, server: { port: 5001, // 使用不同于主应用的端口 cors: true, // 启用CORS origin: 'http://localhost:5001' }, -}) +}) + ``` ## 5. 组件开发规范 diff --git a/examples/plugin-component/src/main.js b/examples/plugin-component/src/main.js index e7e166b1..e99ddf32 100644 --- a/examples/plugin-component/src/main.js +++ b/examples/plugin-component/src/main.js @@ -3,15 +3,16 @@ import App from './App.vue' import { createVuetify } from 'vuetify' import * as components from 'vuetify/components' import * as directives from 'vuetify/directives' +import defaults from './vuetify/defaults' +import theme from './vuetify/theme' import 'vuetify/styles' // 创建Vuetify实例 const vuetify = createVuetify({ components, directives, - theme: { - defaultTheme: 'dark' - } + theme, + defaults }) // 创建应用 diff --git a/examples/plugin-component/src/vuetify/defaults.ts b/examples/plugin-component/src/vuetify/defaults.ts new file mode 100644 index 00000000..3ad423a9 --- /dev/null +++ b/examples/plugin-component/src/vuetify/defaults.ts @@ -0,0 +1,148 @@ +export default { + IconBtn: { + icon: true, + color: 'default', + variant: 'text', + VIcon: { + size: 24, + }, + }, + VAlert: { + VBtn: { + color: undefined, + }, + }, + VAvatar: { + // ℹ️ Remove after next release + variant: 'flat', + VIcon: { + size: 24, + }, + }, + VBadge: { + // set v-badge default color to primary + color: 'primary', + }, + VBtn: { + // set v-btn default color to primary + color: 'primary', + elevation: 0, + }, + VCard: { + elevation: 0, + rounded: 'lg', + }, + VMenu: { + elevation: 0, + }, + VChip: { + elevation: 0, + }, + VBottomSheet: { + elevation: 0, + }, + VDialog: { + elevation: 0, + rounded: 'lg', + }, + VExpansionPanels: { + elevation: 0, + }, + VList: { + color: 'primary', + elevation: 0, + }, + VListItem: { + rounded: 'md', + }, + VPagination: { + activeColor: 'primary', + }, + VTabs: { + // set v-tabs default color to primary + color: 'primary', + VSlideGroup: { + showArrows: true, + }, + }, + VTooltip: { + // set v-tooltip default location to top + location: 'top', + }, + VCheckboxBtn: { + color: 'primary', + hideDetails: 'auto', + }, + VCheckbox: { + // set v-checkbox default color to primary + color: 'primary', + hideDetails: 'auto', + }, + VRadioGroup: { + color: 'primary', + hideDetails: 'auto', + }, + VRadio: { + color: 'primary', + hideDetails: 'auto', + }, + VSelect: { + variant: 'outlined', + color: 'primary', + hideDetails: 'auto', + menuProps: { elevation: 0 }, + }, + VRangeSlider: { + // set v-range-slider default color to primary + color: 'primary', + density: 'comfortable', + thumbLabel: true, + hideDetails: 'auto', + }, + VRating: { + // set v-rating default color to primary + color: 'rgba(var(--v-theme-on-background),0.23)', + activeColor: 'warning', + halfIncrements: true, + }, + VProgressCircular: { + // set v-progress-circular default color to primary + color: 'primary', + }, + VSlider: { + // set v-slider default color to primary + color: 'primary', + hideDetails: 'auto', + }, + VTextField: { + variant: 'outlined', + color: 'primary', + hideDetails: 'auto', + }, + VAutocomplete: { + variant: 'outlined', + color: 'primary', + hideDetails: 'auto', + }, + VCombobox: { + variant: 'outlined', + color: 'primary', + hideDetails: 'auto', + menuProps: { elevation: 0 }, + }, + VFileInput: { + variant: 'outlined', + color: 'primary', + hideDetails: 'auto', + }, + VTextarea: { + variant: 'outlined', + color: 'primary', + hideDetails: 'auto', + }, + VSwitch: { + // set v-switch default color to primary + color: 'primary', + hideDetails: 'auto', + }, +} diff --git a/examples/plugin-component/src/vuetify/theme.ts b/examples/plugin-component/src/vuetify/theme.ts new file mode 100644 index 00000000..23b505c1 --- /dev/null +++ b/examples/plugin-component/src/vuetify/theme.ts @@ -0,0 +1,216 @@ +import type { VuetifyOptions } from 'vuetify' + +const theme: VuetifyOptions['theme'] = { + defaultTheme: 'light', + themes: { + light: { + dark: false, + colors: { + 'primary': '#9155FD', + 'secondary': '#8A8D93', + 'on-secondary': '#FFFFFF', + 'success': '#56CA00', + 'info': '#16B1FF', + 'warning': '#FFB400', + 'error': '#FF4C51', + 'on-primary': '#FFFFFF', + 'on-success': '#FFFFFF', + 'on-warning': '#FFFFFF', + 'background': '#F4F5FA', + 'on-background': '#3A3541', + 'on-surface': '#3A3541', + 'grey-50': '#FAFAFA', + 'grey-100': '#F0F2F8', + 'grey-200': '#EEEEEE', + 'grey-300': '#E0E0E0', + 'grey-400': '#BDBDBD', + 'grey-500': '#9E9E9E', + 'grey-600': '#757575', + 'grey-700': '#616161', + 'grey-800': '#424242', + 'grey-900': '#212121', + 'perfect-scrollbar-thumb': '#DBDADE', + 'skin-bordered-background': '#FFFFFF', + 'skin-bordered-surface': '#FFFFFF', + }, + + variables: { + 'code-color': '#D400FF', + 'overlay-scrim-background': '#3A3541', + 'overlay-scrim-opacity': 0.5, + 'hover-opacity': 0.04, + 'focus-opacity': 0.1, + 'selected-opacity': 0.12, + 'activated-opacity': 0.1, + 'pressed-opacity': 0.14, + 'dragged-opacity': 0.1, + 'border-color': '#3A3541', + 'table-header-background': '#F9FAFC', + 'custom-background': '#F9F8F9', + + // Shadows + 'shadow-key-umbra-opacity': 'rgba(var(--v-theme-on-surface), 0.08)', + 'shadow-key-penumbra-opacity': 'rgba(var(--v-theme-on-surface), 0.12)', + 'shadow-key-ambient-opacity': 'rgba(var(--v-theme-on-surface), 0.04)', + }, + }, + dark: { + dark: true, + colors: { + 'primary': '#6E66ED', + 'secondary': '#8A8D93', + 'on-secondary': '#FFFFFF', + 'success': '#56CA00', + 'info': '#16B1FF', + 'warning': '#FFB400', + 'error': '#FF4C51', + 'on-primary': '#FFFFFF', + 'on-success': '#FFFFFF', + 'on-warning': '#FFFFFF', + 'background': '#0E1116', + 'on-background': '#E7E3FC', + 'surface': '#14161F', + 'on-surface': '#E7E3FC', + 'grey-50': '#2A2E42', + 'grey-100': '#474360', + 'grey-200': '#4A5072', + 'grey-300': '#5E6692', + 'grey-400': '#7983BB', + 'grey-500': '#8692D0', + 'grey-600': '#AAB3DE', + 'grey-700': '#B6BEE3', + 'grey-800': '#CFD3EC', + 'grey-900': '#E7E9F6', + 'perfect-scrollbar-thumb': '#4A5072', + 'skin-bordered-background': '#312d4b', + 'skin-bordered-surface': '#312d4b', + }, + variables: { + 'code-color': '#d400ff', + 'overlay-scrim-background': '#191D21', + 'overlay-scrim-opacity': 0.6, + 'hover-opacity': 0.04, + 'focus-opacity': 0.1, + 'selected-opacity': 0.12, + 'activated-opacity': 0.1, + 'pressed-opacity': 0.14, + 'dragged-opacity': 0.1, + 'border-color': '#E7E3FC', + 'table-header-background': '#14161F', + 'custom-background': '#373452', + // Shadows + 'shadow-key-umbra-opacity': 'rgba(20, 18, 33, 0.08)', + 'shadow-key-penumbra-opacity': 'rgba(20, 18, 33, 0.12)', + 'shadow-key-ambient-opacity': 'rgba(20, 18, 33, 0.04)', + }, + }, + purple: { + dark: true, + colors: { + 'primary': '#9155FD', + 'secondary': '#8A8D93', + 'on-secondary': '#FFFFFF', + 'success': '#56CA00', + 'info': '#16B1FF', + 'warning': '#FFB400', + 'error': '#FF4C51', + 'on-primary': '#FFFFFF', + 'on-success': '#FFFFFF', + 'on-warning': '#FFFFFF', + 'background': '#28243D', + 'on-background': '#E7E3FC', + 'surface': '#312D4B', + 'on-surface': '#E7E3FC', + 'grey-50': '#2A2E42', + 'grey-100': '#474360', + 'grey-200': '#4A5072', + 'grey-300': '#5E6692', + 'grey-400': '#7983BB', + 'grey-500': '#8692D0', + 'grey-600': '#AAB3DE', + 'grey-700': '#B6BEE3', + 'grey-800': '#CFD3EC', + 'grey-900': '#E7E9F6', + 'perfect-scrollbar-thumb': '#4A5072', + 'skin-bordered-background': '#312d4b', + 'skin-bordered-surface': '#312d4b', + }, + variables: { + 'code-color': '#d400ff', + 'overlay-scrim-background': '#2C2942', + 'overlay-scrim-opacity': 0.6, + 'hover-opacity': 0.04, + 'focus-opacity': 0.1, + 'selected-opacity': 0.12, + 'activated-opacity': 0.1, + 'pressed-opacity': 0.14, + 'dragged-opacity': 0.1, + 'border-color': '#E7E3FC', + 'table-header-background': '#3D3759', + 'custom-background': '#373452', + + // Shadows + 'shadow-key-umbra-opacity': 'rgba(20, 18, 33, 0.08)', + 'shadow-key-penumbra-opacity': 'rgba(20, 18, 33, 0.12)', + 'shadow-key-ambient-opacity': 'rgba(20, 18, 33, 0.04)', + }, + }, + transparent: { + dark: true, + colors: { + 'primary': '#A370F7', + 'secondary': '#8A8D93', + 'on-secondary': '#FFFFFF', + 'success': '#66BB6A', + 'info': '#42A5F5', + 'warning': '#FFA726', + 'error': '#EF5350', + 'on-primary': '#FFFFFF', + 'on-success': '#FFFFFF', + 'on-warning': '#FFFFFF', + 'background': '#000000', + 'on-background': '#E7E3FC', + 'surface': 'rgba(30, 30, 30, 0.3)', + 'on-surface': '#E7E3FC', + 'surface-variant': 'rgba(30, 30, 30, 0.2)', + 'on-surface-variant': 'rgba(255, 255, 255, 0.65)', + 'grey-50': 'rgba(42, 46, 66, 0.15)', + 'grey-100': 'rgba(71, 67, 96, 0.15)', + 'grey-200': 'rgba(74, 80, 114, 0.15)', + 'grey-300': 'rgba(94, 102, 146, 0.15)', + 'grey-400': 'rgba(121, 131, 187, 0.15)', + 'grey-500': 'rgba(134, 146, 208, 0.15)', + 'grey-600': 'rgba(170, 179, 222, 0.15)', + 'grey-700': 'rgba(182, 190, 227, 0.15)', + 'grey-800': 'rgba(207, 211, 236, 0.15)', + 'grey-900': 'rgba(231, 233, 246, 0.15)', + 'perfect-scrollbar-thumb': 'rgba(158, 158, 190, 0.4)', + 'skin-bordered-background': 'rgba(30, 30, 30, 0.3)', + 'skin-bordered-surface': 'rgba(30, 30, 30, 0.3)', + 'card-background': 'rgba(30, 30, 30, 0.3)', + }, + variables: { + 'code-color': '#6D9EEB', + 'overlay-scrim-background': '0, 0, 0', + 'overlay-scrim-opacity': 0.7, + 'hover-opacity': 0.1, + 'focus-opacity': 0.15, + 'selected-opacity': 0.2, + 'activated-opacity': 0.15, + 'pressed-opacity': 0.2, + 'dragged-opacity': 0.15, + 'border-color': '#E7E3FC', + 'table-header-background': 'rgba(30, 30, 30, 0.3)', + 'custom-background': 'rgba(30, 30, 30, 0.3)', + 'card-background': 'rgba(30, 30, 30, 0.3)', + + // Shadows + 'shadow-key-umbra-opacity': 'rgba(0, 0, 0, 0.07)', + 'shadow-key-penumbra-opacity': 'rgba(0, 0, 0, 0.1)', + 'shadow-key-ambient-opacity': 'rgba(0, 0, 0, 0.05)', + }, + }, + }, +} + +export default theme diff --git a/examples/plugin-component/vite.config.js b/examples/plugin-component/vite.config.js index 1fd8c07d..e3a20781 100644 --- a/examples/plugin-component/vite.config.js +++ b/examples/plugin-component/vite.config.js @@ -21,10 +21,12 @@ export default defineConfig({ vuetify: { requiredVersion: false, generate: false, + singleton: true, }, 'vuetify/styles': { requiredVersion: false, generate: false, + singleton: true, }, }, format: 'esm' @@ -33,7 +35,48 @@ export default defineConfig({ build: { target: 'esnext', // 必须设置为esnext以支持顶层await minify: false, // 开发阶段建议关闭混淆 - cssCodeSplit: false, + cssCodeSplit: true, // 改为true以便能分离样式文件 + rollupOptions: { + output: { + manualChunks: { + 'vuetify-lib': ['vuetify'] // 将vuetify单独分离出来 + } + } + } + }, + css: { + preprocessorOptions: { + scss: { + additionalData: '/* 覆盖vuetify样式 */', + } + }, + postcss: { + plugins: [ + { + postcssPlugin: 'internal:charset-removal', + AtRule: { + charset: (atRule) => { + if (atRule.name === 'charset') { + atRule.remove(); + } + } + } + }, + { + postcssPlugin: 'vuetify-filter', + Root(root) { + // 过滤掉所有vuetify相关的CSS + root.walkRules(rule => { + if (rule.selector && ( + rule.selector.includes('.v-') || + rule.selector.includes('.mdi-'))) { + rule.remove(); + } + }); + } + } + ] + } }, server: { port: 5001, // 使用不同于主应用的端口 diff --git a/package.json b/package.json index e87a57c0..2464e1eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "moviepilot", - "version": "2.4.4-1", + "version": "2.4.5", "private": true, "type": "module", "bin": "dist/service.js", diff --git a/src/components/cards/CustomRuleCard.vue b/src/components/cards/CustomRuleCard.vue index 90467aa9..419429d4 100644 --- a/src/components/cards/CustomRuleCard.vue +++ b/src/components/cards/CustomRuleCard.vue @@ -107,7 +107,7 @@ function onClose() { - + diff --git a/src/components/cards/FilterRuleGroupCard.vue b/src/components/cards/FilterRuleGroupCard.vue index 58198c5e..cf29c568 100644 --- a/src/components/cards/FilterRuleGroupCard.vue +++ b/src/components/cards/FilterRuleGroupCard.vue @@ -220,7 +220,7 @@ function onClose() { - + diff --git a/src/components/cards/MediaServerCard.vue b/src/components/cards/MediaServerCard.vue index 2dcb4350..e648113e 100644 --- a/src/components/cards/MediaServerCard.vue +++ b/src/components/cards/MediaServerCard.vue @@ -200,7 +200,7 @@ onMounted(() => { - + diff --git a/src/components/cards/NotificationChannelCard.vue b/src/components/cards/NotificationChannelCard.vue index 41b9e80e..324219b6 100644 --- a/src/components/cards/NotificationChannelCard.vue +++ b/src/components/cards/NotificationChannelCard.vue @@ -135,7 +135,7 @@ function onClose() { - + diff --git a/src/components/cards/SiteCard.vue b/src/components/cards/SiteCard.vue index a1f91301..5592a058 100644 --- a/src/components/cards/SiteCard.vue +++ b/src/components/cards/SiteCard.vue @@ -293,21 +293,20 @@ onMounted(() => { - +
@@ -322,29 +321,29 @@ onMounted(() => {
- - + + - - + + - - + + {{ t('site.browseResources') }} {{ t('site.deleteSite') }} @@ -386,12 +385,6 @@ onMounted(() => { diff --git a/src/components/dialog/AlistConfigDialog.vue b/src/components/dialog/AlistConfigDialog.vue index df672f91..32e2264d 100644 --- a/src/components/dialog/AlistConfigDialog.vue +++ b/src/components/dialog/AlistConfigDialog.vue @@ -69,7 +69,7 @@ async function savaAlistConfig() {