From 14f0b6cd333b18bc4bac992be5a985a5c0e8e18e Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sun, 10 Aug 2025 11:51:32 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat=20(core):=20=E6=8B=86=E5=88=86=20Modal?= =?UTF-8?q?=20=E4=B8=BA=E7=8B=AC=E7=AB=8B=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docs.yml | 36 +---- .gitignore | 1 + config/config.json | 18 +++ src/components/About.vue | 34 ++--- src/components/Settings.vue | 190 ++++++++++++-------------- src/ui/Modal.vue | 264 ++++++++++++++++++++++++++++++++++++ src/ui/Select.vue | 192 +++++++++++++++++--------- 7 files changed, 515 insertions(+), 220 deletions(-) create mode 100644 config/config.json create mode 100644 src/ui/Modal.vue diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 991fb2d..197185b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,10 +1,13 @@ -name: Publish Docs +name: Publish docs via GitHub Pages on: push: + pull_request: + types: [closed] + workflow_dispatch: jobs: - test: + deploy: runs-on: ubuntu-latest strategy: matrix: @@ -12,41 +15,16 @@ jobs: steps: - uses: actions/checkout@v3 - with: - fetch-depth: 0 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - - name: Install pnpm - uses: pnpm/action-setup@v2 - with: - version: 8 - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - name: Install dependencies - run: pnpm install - - - name: Link package globally - run: | - pnpm link --global - pnpm link --global pageforge + run: npm install pageforge -g - - name: Test build command + - name: Build run: | cd docs pageforge build diff --git a/.gitignore b/.gitignore index e7b8986..1b41949 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ dist-ssr pnpm-lock.yaml *.csv *.json +!config*.json diff --git a/config/config.json b/config/config.json new file mode 100644 index 0000000..faa0113 --- /dev/null +++ b/config/config.json @@ -0,0 +1,18 @@ +{ + "log_directory": null, + "auto_clear_logs": true, + "keep_log_days": 30, + "theme": "system", + "plugins": [ + { + "enabled": true, + "execute_home": null, + "extensions": [], + "language": "python2", + "before_compile": null, + "after_compile": null, + "run_command": null, + "template": null + } + ] +} \ No newline at end of file diff --git a/src/components/About.vue b/src/components/About.vue index 8cd1474..d697e89 100644 --- a/src/components/About.vue +++ b/src/components/About.vue @@ -1,26 +1,6 @@ diff --git a/src/ui/Select.vue b/src/ui/Select.vue index 52f7b04..d1d7236 100644 --- a/src/ui/Select.vue +++ b/src/ui/Select.vue @@ -2,6 +2,7 @@
- - -
- -
- -
- - - - -
- {{ noOptionsText }} + + + + +
+ {{ noOptionsText }} +
-
-
+ +
@@ -154,9 +159,14 @@ const isOpen = ref(false) const searchQuery = ref('') const highlightedIndex = ref(-1) const selectContainer = ref() +const selectButton = ref() +const dropdown = ref() const searchInput = ref() const buttonId = `select-button-${ Math.random().toString(36).substr(2, 9) }` +// 下拉框位置样式 +const dropdownStyle = ref>({}) + // 计算属性 const normalizedOptions = computed(() => { return props.options.map(option => { @@ -193,6 +203,40 @@ const selectedLabel = computed(() => { return selectedOption.value?.label || '' }) +// 更新下拉框位置 +const updateDropdownPosition = async () => { + if (!selectButton.value || !isOpen.value) { + return + } + + await nextTick() + + const buttonRect = selectButton.value.getBoundingClientRect() + const viewportHeight = window.innerHeight + + // 计算下方可用空间 + const spaceBelow = viewportHeight - buttonRect.bottom + const dropdownHeight = 240 // max-h-60 对应约240px + + let top = buttonRect.bottom + 2 // 默认显示在下方 + let left = buttonRect.left + let width = buttonRect.width + + // 如果下方空间不足,显示在上方 + if (spaceBelow < dropdownHeight && buttonRect.top > dropdownHeight) { + // 显示在上方时,让下拉框紧贴按钮顶部 + top = buttonRect.top - 2 + } + + dropdownStyle.value = { + top: `${ top }px`, + left: `${ left }px`, + width: `${ width }px`, + minWidth: `${ width }px`, + transform: spaceBelow < dropdownHeight && buttonRect.top > dropdownHeight ? 'translateY(-100%)' : 'none' + } +} + // 方法 const getOptionValue = (option: Option) => option.value const getOptionLabel = (option: Option) => option.label @@ -201,7 +245,7 @@ const isSelected = (option: Option) => { return option.value === props.modelValue } -const toggleDropdown = () => { +const toggleDropdown = async () => { if (props.disabled) { return } @@ -214,7 +258,7 @@ const toggleDropdown = () => { } } -const openDropdown = () => { +const openDropdown = async () => { if (props.disabled) { return } @@ -222,17 +266,30 @@ const openDropdown = () => { isOpen.value = true highlightedIndex.value = -1 + // 更新位置 + await updateDropdownPosition() + + // 监听滚动和窗口大小变化 + window.addEventListener('scroll', updateDropdownPosition, true) + window.addEventListener('resize', updateDropdownPosition) + if (props.searchable) { nextTick(() => { searchInput.value?.focus() }) } + + emit('before-open') } const closeDropdown = () => { isOpen.value = false searchQuery.value = '' highlightedIndex.value = -1 + + // 移除监听器 + window.removeEventListener('scroll', updateDropdownPosition, true) + window.removeEventListener('resize', updateDropdownPosition) } const selectOption = (option: Option) => { @@ -245,6 +302,12 @@ const selectOption = (option: Option) => { closeDropdown() } +// 处理进入动画前的事件 +const handleBeforeEnter = () => { + updateDropdownPosition() + emit('before-open') +} + // 键盘导航 const handleKeydown = (event: KeyboardEvent) => { if (!isOpen.value) { @@ -278,7 +341,12 @@ const handleKeydown = (event: KeyboardEvent) => { // 点击外部关闭 const handleClickOutside = (event: Event) => { - if (selectContainer.value && !selectContainer.value.contains(event.target as Node)) { + if ( + selectContainer.value && + !selectContainer.value.contains(event.target as Node) && + dropdown.value && + !dropdown.value.contains(event.target as Node) + ) { closeDropdown() } } @@ -298,5 +366,7 @@ onMounted(() => { onUnmounted(() => { document.removeEventListener('click', handleClickOutside) document.removeEventListener('keydown', handleKeydown) + window.removeEventListener('scroll', updateDropdownPosition, true) + window.removeEventListener('resize', updateDropdownPosition) }) From b5d89a1768fc2ee98f85c4a59cbfdf42a3bc45df Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sun, 10 Aug 2025 11:53:10 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix=20(core):=20=E4=BF=AE=E5=A4=8D=20Toast?= =?UTF-8?q?=20=E5=B1=95=E7=A4=BA=E7=AA=97=E5=8F=A3=E4=BC=98=E5=85=88?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Toast.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Toast.vue b/src/components/Toast.vue index 02d5099..ebb7bf2 100644 --- a/src/components/Toast.vue +++ b/src/components/Toast.vue @@ -1,6 +1,6 @@