diff --git a/bun.lockb b/bun.lockb index 28847b1..ccf46f9 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/default.d.ts b/default.d.ts index 391c8cf..16e885a 100644 --- a/default.d.ts +++ b/default.d.ts @@ -31,3 +31,13 @@ declare global { $dialog: DialogProviderInst } } + +// Vite worker 与样式类型声明 +declare module '*?worker' { + const workerConstructor: { new(): Worker } + export default workerConstructor +} +declare module '*.css' { + const content: string + export default content +} diff --git a/package.json b/package.json index 8064826..913b6a7 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "knip": "knip" }, "dependencies": { - "@guolao/vue-monaco-editor": "^1.5.5", "@hyperdx/browser": "^0.21.2", "@hyperdx/cli": "^0.1.0", "@microsoft/signalr": "^9.0.6", @@ -52,6 +51,7 @@ "jszip": "^3.10.1", "linqts": "^3.2.0", "lodash": "^4.17.21", + "lodash-es": "^4.17.21", "md5": "^2.3.0", "mitt": "^3.0.1", "monaco-editor": "^0.53.0", @@ -65,6 +65,7 @@ "unplugin-vue-markdown": "^29.2.0", "uuid": "^13.0.0", "vite": "npm:rolldown-vite@latest", + "vite-plugin-monaco-editor-nls": "^3.0.1", "vite-svg-loader": "^5.1.0", "vue": "3.5.22", "vue-cropperjs": "^5.0.0", @@ -94,6 +95,7 @@ "stylus": "^0.64.0", "typescript": "^5.9.2", "vite-plugin-cdn-import": "^1.0.1", + "vscode-loc": "git+https://github.com/microsoft/vscode-loc.git", "vue-vine": "^1.7.6" } } diff --git a/src/App.vue b/src/App.vue index 4a66542..1a27409 100644 --- a/src/App.vue +++ b/src/App.vue @@ -12,16 +12,18 @@ import { NSpin, zhCN, } from 'naive-ui' -import { computed } from 'vue' +import { computed, defineAsyncComponent } from 'vue' import { useRoute } from 'vue-router' -import ManageLayout from '@/views/ManageLayout.vue' -import ViewerLayout from '@/views/ViewerLayout.vue' import { ThemeType } from './api/api-models' -import ClientLayout from './client/ClientLayout.vue' import TempComponent from './components/TempComponent.vue' import { isDarkMode, theme } from './Utils' -import OBSLayout from './views/OBSLayout.vue' -import OpenLiveLayout from './views/OpenLiveLayout.vue' + +// 将大型布局组件改为异步组件,避免打入入口包 +const ManageLayout = defineAsyncComponent(() => import('@/views/ManageLayout.vue')) +const ViewerLayout = defineAsyncComponent(() => import('@/views/ViewerLayout.vue')) +const ClientLayout = defineAsyncComponent(() => import('./client/ClientLayout.vue')) +const OBSLayout = defineAsyncComponent(() => import('./views/OBSLayout.vue')) +const OpenLiveLayout = defineAsyncComponent(() => import('./views/OpenLiveLayout.vue')) const route = useRoute() const themeType = useStorage('Settings.Theme', ThemeType.Auto) diff --git a/src/components.d.ts b/src/components.d.ts index 382f8ff..09b830b 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -19,19 +19,13 @@ declare module 'vue' { LiveInfoContainer: typeof import('./components/LiveInfoContainer.vue')['default'] MonacoEditorComponent: typeof import('./components/MonacoEditorComponent.vue')['default'] NAlert: typeof import('naive-ui')['NAlert'] - NAvatar: typeof import('naive-ui')['NAvatar'] - NButton: typeof import('naive-ui')['NButton'] - NCard: typeof import('naive-ui')['NCard'] NEllipsis: typeof import('naive-ui')['NEllipsis'] NEmpty: typeof import('naive-ui')['NEmpty'] NFlex: typeof import('naive-ui')['NFlex'] NFormItemGi: typeof import('naive-ui')['NFormItemGi'] NGridItem: typeof import('naive-ui')['NGridItem'] NIcon: typeof import('naive-ui')['NIcon'] - NImage: typeof import('naive-ui')['NImage'] - NPopconfirm: typeof import('naive-ui')['NPopconfirm'] NScrollbar: typeof import('naive-ui')['NScrollbar'] - NSpace: typeof import('naive-ui')['NSpace'] NTag: typeof import('naive-ui')['NTag'] NText: typeof import('naive-ui')['NText'] NTime: typeof import('naive-ui')['NTime'] diff --git a/src/components/MonacoEditorComponent.vue b/src/components/MonacoEditorComponent.vue index 4adbf93..195265d 100644 --- a/src/components/MonacoEditorComponent.vue +++ b/src/components/MonacoEditorComponent.vue @@ -1,66 +1,121 @@ diff --git a/src/components/ScheduleList.vue b/src/components/ScheduleList.vue index 451fcc2..fd0175e 100644 --- a/src/components/ScheduleList.vue +++ b/src/components/ScheduleList.vue @@ -20,24 +20,34 @@ const themeVars = useThemeVars() const weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] +// 常量定义 +const MILLISECONDS_PER_DAY = 86400000 + function getISOWeek(date: Date) { const target = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())) const dayNumber = target.getUTCDay() || 7 target.setUTCDate(target.getUTCDate() + 4 - dayNumber) const yearStart = new Date(Date.UTC(target.getUTCFullYear(), 0, 1)) - const week = Math.ceil(((target.getTime() - yearStart.getTime()) / 86400000 + 1) / 7) + const week = Math.ceil(((target.getTime() - yearStart.getTime()) / MILLISECONDS_PER_DAY + 1) / 7) return { year: target.getUTCFullYear(), week, } } -const currentISOWeek = getISOWeek(new Date()) +const now = new Date() +const currentISOWeek = getISOWeek(now) +const currentDayOfWeek = (now.getDay() + 6) % 7 // 转换为周一=0的格式 function isCurrentWeek(year: number, week: number) { return year === currentISOWeek.year && week === currentISOWeek.week } +function isCurrentDay(year: number, week: number, dayIndex: number) { + if (!isCurrentWeek(year, week)) return false + return dayIndex === currentDayOfWeek +} + const dateFormatter = new Intl.DateTimeFormat('zh-CN', { month: '2-digit', day: '2-digit', @@ -51,17 +61,40 @@ function getWeekRangeLabel(year: number, week: number) { function getDateFromWeek(year: number, week: number, dayOfWeek: number): Date { // week starts from 1-52, dayOfWeek starts from 0-6 where 0 is Monday - const simple = new Date(year, 0, 1 + (week - 1) * 7) - const dow = simple.getDay() - const ISOweekStart = simple - if (dow <= 4) ISOweekStart.setDate(simple.getDate() - simple.getDay() + 1) - else ISOweekStart.setDate(simple.getDate() + 8 - simple.getDay()) - return new Date(ISOweekStart.getFullYear(), ISOweekStart.getMonth(), ISOweekStart.getDate() + dayOfWeek) + const januaryFourth = new Date(year, 0, 4) + const startOfWeekOne = new Date(januaryFourth) + const dayOfWeekJan4 = (januaryFourth.getDay() + 6) % 7 + startOfWeekOne.setDate(januaryFourth.getDate() - dayOfWeekJan4) + + const targetDate = new Date(startOfWeekOne) + targetDate.setDate(startOfWeekOne.getDate() + (week - 1) * 7 + dayOfWeek) + return targetDate +} + +// 样式工具函数 +function getDayHeaderStyle(year: number, week: number, dayIndex: number, primaryColor: string, primaryColorSuppl: string) { + const isToday = isCurrentDay(year, week, dayIndex) + + return { + marginBottom: '6px', + padding: '4px 8px', + fontSize: '13px', + fontWeight: '600', + background: isToday + ? `linear-gradient(135deg, ${primaryColor}25 0%, ${primaryColor}40 100%)` + : `linear-gradient(135deg, ${primaryColorSuppl}15 0%, ${primaryColorSuppl}25 100%)`, + borderRadius: '4px', + display: 'flex', + alignItems: 'center', + gap: '6px', + boxShadow: isToday ? `0 0 0 2px ${primaryColor}66` : undefined, + transition: 'all 0.3s ease', + } } + + diff --git a/src/views/pointViews/PointUserSettings.vue b/src/views/pointViews/PointUserSettings.vue index 85671a7..ef68e82 100644 --- a/src/views/pointViews/PointUserSettings.vue +++ b/src/views/pointViews/PointUserSettings.vue @@ -274,55 +274,71 @@ defineExpose({ - + 添加地址 + @@ -333,59 +349,93 @@ defineExpose({ title="登录链接" name="2" > - + + + 使用此链接可以直接登录到您的账号 + + + - + 确定要登出吗? - - 切换账号 - - + 切换账号 + + + - - - {{ item.name }} - - - {{ item.uId }} - - - - + + + 当前账号 + + + {{ item.name }} + + + + {{ item.uId }} + + + + + + @@ -403,18 +453,23 @@ defineExpose({ ref="formRef" :model="currentAddress" :rules="rules" + label-placement="top" > - + @@ -423,8 +478,8 @@ defineExpose({ v-model:value="currentAddress.city" :options="cityOptions(currentAddress.province)" :disabled="!currentAddress?.province" - placeholder="请选择市" - style="width: 100px" + placeholder="市" + style="flex: 1; min-width: 100px" filterable @update:value="onAreaSelectChange(1)" /> @@ -433,8 +488,8 @@ defineExpose({ v-model:value="currentAddress.district" :options="currentAddress.city ? districtOptions(currentAddress.province, currentAddress.city) : []" :disabled="!currentAddress?.city" - placeholder="请选择区" - style="width: 100px" + placeholder="区" + style="flex: 1; min-width: 100px" filterable @update:value="onAreaSelectChange(2)" /> @@ -443,8 +498,8 @@ defineExpose({ v-model:value="currentAddress.street" :options="currentAddress.city && currentAddress.district ? streetOptions(currentAddress.province, currentAddress.city, currentAddress.district) : []" :disabled="!currentAddress?.district" - placeholder="请选择街道" - style="width: 150px" + placeholder="街道" + style="flex: 1; min-width: 120px" filterable /> @@ -456,39 +511,43 @@ defineExpose({ > - - - - - - + + + + + + + + - 阅读并同意本站 + 我已阅读并同意本站 - - 保存 - + + 取消 + + + 保存 + + @@ -519,3 +588,37 @@ defineExpose({ + + diff --git a/vite.config.mts b/vite.config.mts index b9701a4..04889b0 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -9,6 +9,7 @@ import Markdown from 'unplugin-vue-markdown/vite' import { defineConfig } from 'vite' import svgLoader from 'vite-svg-loader' import { VineVitePlugin } from 'vue-vine/vite' +// import MonacoEditorNlsPlugin, { esbuildPluginMonacoEditorNls, Languages } from 'vite-plugin-monaco-editor-nls' // 自定义SVGO插件,删除所有名称以sodipodi:和inkscape:开头的元素 const removeSodipodiInkscape = { @@ -87,6 +88,8 @@ export default defineConfig({ include: [/\.vue$/, /\.vue\?vue/, /\.md$/, /\.vine$/], }), VineVitePlugin(), + // Monaco 中文本地化 + // MonacoEditorNlsPlugin({ locale: Languages.zh_hans }), ], server: { port: 51000 }, resolve: { alias: { '@': path.resolve(__dirname, 'src') } }, @@ -97,9 +100,15 @@ export default defineConfig({ }, optimizeDeps: { include: ['@vicons/fluent', '@vicons/ionicons5', 'vue', 'vue-router'], + esbuildOptions: { + // plugins: [ + // esbuildPluginMonacoEditorNls({ locale: Languages.zh_hans }), + // ], + }, }, build: { - sourcemap: true, + // 生产环境建议关闭以减少产物体积与网络请求 + sourcemap: false, target: 'esnext', minify: 'oxc', chunkSizeWarningLimit: 1000, @@ -117,6 +126,37 @@ export default defineConfig({ test: /[\\/]node_modules[\\/](naive-ui|@vueuse[\\/]core)[\\/]/, priority: -10, }, + // 精细化切分大体积依赖,提升缓存与首屏体积可控性 + { + name: 'echarts-vendor', + test: /[\\/]node_modules[\\/](echarts|zrender|vue-echarts)[\\/]/, + priority: -20, + }, + { + name: 'wangeditor-vendor', + test: /[\\/]node_modules[\\/]@wangeditor[\\/]/, + priority: -20, + }, + { + name: 'hyperdx-vendor', + test: /[\\/]node_modules[\\/]@hyperdx[\\/]/, + priority: -20, + }, + { + name: 'xlsx-vendor', + test: /[\\/]node_modules[\\/]xlsx[\\/]/, + priority: -20, + }, + { + name: 'jszip-vendor', + test: /[\\/]node_modules[\\/]jszip[\\/]/, + priority: -20, + }, + { + name: 'html2canvas-vendor', + test: /[\\/]node_modules[\\/]html2canvas[\\/]/, + priority: -20, + }, ], }, },