feat(checkin): 启用多附件功能并添加文本折叠与媒体标签页
- 在环境变量中启用多附件功能,支持图片、视频和音频的标签页切换 - 为打卡卡片添加文本内容折叠/展开功能,当文本溢出时显示"全文/收起"按钮 - 重构媒体展示逻辑,当存在多种媒体类型时使用标签页组织,单一类型时保持原有布局 - 添加单元测试确保组件功能正确性,配置Vitest测试环境 - 更新TypeScript类型定义和开发依赖以支持测试
Showing
9 changed files
with
135 additions
and
3 deletions
| ... | @@ -17,7 +17,7 @@ VITE_CONSOLE = 0 | ... | @@ -17,7 +17,7 @@ VITE_CONSOLE = 0 |
| 17 | VITE_APPID=微信appID | 17 | VITE_APPID=微信appID |
| 18 | 18 | ||
| 19 | # 是否开启多附件功能 | 19 | # 是否开启多附件功能 |
| 20 | -VITE_CHECKIN_MULTI_ATTACHMENT = 0 | 20 | +VITE_CHECKIN_MULTI_ATTACHMENT = 1 |
| 21 | 21 | ||
| 22 | # 是否开启打卡草稿缓存功能 | 22 | # 是否开启打卡草稿缓存功能 |
| 23 | VITE_CHECKIN_DRAFT_CACHE = 0 | 23 | VITE_CHECKIN_DRAFT_CACHE = 0 | ... | ... |
This diff could not be displayed because it is too large.
| ... | @@ -58,9 +58,11 @@ | ... | @@ -58,9 +58,11 @@ |
| 58 | "devDependencies": { | 58 | "devDependencies": { |
| 59 | "@vitejs/plugin-vue": "^5.2.1", | 59 | "@vitejs/plugin-vue": "^5.2.1", |
| 60 | "@vitejs/plugin-vue-jsx": "^4.1.2", | 60 | "@vitejs/plugin-vue-jsx": "^4.1.2", |
| 61 | + "@vue/test-utils": "^2.4.6", | ||
| 61 | "@vueuse/core": "^13.0.0", | 62 | "@vueuse/core": "^13.0.0", |
| 62 | "autoprefixer": "^10.4.19", | 63 | "autoprefixer": "^10.4.19", |
| 63 | "axios": "^1.8.4", | 64 | "axios": "^1.8.4", |
| 65 | + "jsdom": "^24.1.3", | ||
| 64 | "less": "^4.2.2", | 66 | "less": "^4.2.2", |
| 65 | "postcss": "^8.4.35", | 67 | "postcss": "^8.4.35", |
| 66 | "qs": "^6.14.0", | 68 | "qs": "^6.14.0", | ... | ... |
| ... | @@ -71,6 +71,6 @@ declare global { | ... | @@ -71,6 +71,6 @@ declare global { |
| 71 | // for type re-export | 71 | // for type re-export |
| 72 | declare global { | 72 | declare global { |
| 73 | // @ts-ignore | 73 | // @ts-ignore |
| 74 | - export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' | 74 | + export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' |
| 75 | import('vue') | 75 | import('vue') |
| 76 | } | 76 | } | ... | ... |
This diff is collapsed. Click to expand it.
| 1 | +import { mount } from '@vue/test-utils' | ||
| 2 | +import { describe, it, expect } from 'vitest' | ||
| 3 | +import CheckinCard from '../CheckinCard.vue' | ||
| 4 | + | ||
| 5 | +describe('CheckinCard.vue', () => { | ||
| 6 | + const defaultPost = { | ||
| 7 | + id: 1, | ||
| 8 | + user: { name: 'Test User', avatar: '' }, | ||
| 9 | + content: 'Test Content', | ||
| 10 | + images: [], | ||
| 11 | + videoList: [], | ||
| 12 | + audio: [], | ||
| 13 | + likes: 0, | ||
| 14 | + is_liked: false, | ||
| 15 | + is_my: false | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + const globalStubs = { | ||
| 19 | + 'van-image': true, | ||
| 20 | + 'van-row': true, | ||
| 21 | + 'van-col': true, | ||
| 22 | + 'van-icon': true, | ||
| 23 | + 'van-image-preview': true, | ||
| 24 | + 'van-tabs': { template: '<div><slot /></div>' }, | ||
| 25 | + 'van-tab': { name: 'van-tab', template: '<div><slot /></div>', props: ['title'] }, | ||
| 26 | + 'PostCountModel': true, | ||
| 27 | + 'VideoPlayer': true, | ||
| 28 | + 'AudioPlayer': true | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + it('renders content correctly', () => { | ||
| 32 | + const wrapper = mount(CheckinCard, { | ||
| 33 | + props: { post: defaultPost }, | ||
| 34 | + global: { | ||
| 35 | + stubs: globalStubs | ||
| 36 | + } | ||
| 37 | + }) | ||
| 38 | + expect(wrapper.text()).toContain('Test Content') | ||
| 39 | + }) | ||
| 40 | + | ||
| 41 | + it('shows expand button when content overflows', async () => { | ||
| 42 | + const wrapper = mount(CheckinCard, { | ||
| 43 | + props: { post: { ...defaultPost, content: 'Long content...' } }, | ||
| 44 | + global: { | ||
| 45 | + stubs: globalStubs | ||
| 46 | + } | ||
| 47 | + }) | ||
| 48 | + | ||
| 49 | + const textEl = wrapper.find('.post-text').element | ||
| 50 | + Object.defineProperty(textEl, 'scrollHeight', { value: 200, configurable: true }) | ||
| 51 | + Object.defineProperty(textEl, 'clientHeight', { value: 100, configurable: true }) | ||
| 52 | + | ||
| 53 | + await wrapper.setProps({ post: { ...defaultPost, content: 'Updated Long Content' } }) | ||
| 54 | + await wrapper.vm.$nextTick() | ||
| 55 | + }) | ||
| 56 | + | ||
| 57 | + it('shows tabs when multiple media types exist', () => { | ||
| 58 | + const postWithMultiMedia = { | ||
| 59 | + ...defaultPost, | ||
| 60 | + images: ['img1.jpg'], | ||
| 61 | + videoList: [{ id: 1, video: 'vid1.mp4' }], | ||
| 62 | + audio: [] | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + const wrapper = mount(CheckinCard, { | ||
| 66 | + props: { post: postWithMultiMedia }, | ||
| 67 | + global: { | ||
| 68 | + stubs: globalStubs | ||
| 69 | + } | ||
| 70 | + }) | ||
| 71 | + | ||
| 72 | + const tabs = wrapper.findAllComponents({ name: 'van-tab' }) | ||
| 73 | + expect(tabs.length).toBe(2) | ||
| 74 | + expect(tabs[0].props('title')).toBe('图片') | ||
| 75 | + expect(tabs[1].props('title')).toBe('视频') | ||
| 76 | + }) | ||
| 77 | + | ||
| 78 | + it('does not show tabs when single media type exists', () => { | ||
| 79 | + const postWithSingleMedia = { | ||
| 80 | + ...defaultPost, | ||
| 81 | + images: ['img1.jpg'], | ||
| 82 | + videoList: [], | ||
| 83 | + audio: [] | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + const wrapper = mount(CheckinCard, { | ||
| 87 | + props: { post: postWithSingleMedia }, | ||
| 88 | + global: { | ||
| 89 | + stubs: globalStubs | ||
| 90 | + } | ||
| 91 | + }) | ||
| 92 | + | ||
| 93 | + const tabs = wrapper.findAllComponents({ name: 'van-tab' }) | ||
| 94 | + expect(tabs.length).toBe(0) | ||
| 95 | + expect(wrapper.find('.post-images').exists()).toBe(true) | ||
| 96 | + }) | ||
| 97 | +}) |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-05-29 15:34:17 | 2 | * @Date: 2025-05-29 15:34:17 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2026-01-24 15:29:28 | 4 | + * @LastEditTime: 2026-01-26 09:52:40 |
| 5 | * @FilePath: /mlaj/src/views/checkin/IndexCheckInPage.vue | 5 | * @FilePath: /mlaj/src/views/checkin/IndexCheckInPage.vue |
| 6 | * @Description: 用户打卡主页 | 6 | * @Description: 用户打卡主页 |
| 7 | --> | 7 | --> | ... | ... |
vitest.config.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2026-01-26 13:31:38 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2026-01-26 13:33:54 | ||
| 5 | + * @FilePath: /mlaj/vitest.config.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +import { defineConfig } from 'vitest/config' | ||
| 9 | +import vue from '@vitejs/plugin-vue' | ||
| 10 | +import path from 'path' | ||
| 11 | + | ||
| 12 | +export default defineConfig({ | ||
| 13 | + plugins: [vue()], | ||
| 14 | + resolve: { | ||
| 15 | + alias: { | ||
| 16 | + "@": path.resolve(__dirname, "src"), | ||
| 17 | + } | ||
| 18 | + }, | ||
| 19 | + test: { | ||
| 20 | + environment: 'jsdom', | ||
| 21 | + css: { | ||
| 22 | + include: [], | ||
| 23 | + modules: { | ||
| 24 | + classNameStrategy: 'non-scoped' | ||
| 25 | + } | ||
| 26 | + }, | ||
| 27 | + server: { | ||
| 28 | + deps: { | ||
| 29 | + inline: ['vant'] | ||
| 30 | + } | ||
| 31 | + } | ||
| 32 | + } | ||
| 33 | +}) |
This diff could not be displayed because it is too large.
-
Please register or login to post a comment