hookehuyr

feat(checkin): 启用多附件功能并添加文本折叠与媒体标签页

- 在环境变量中启用多附件功能,支持图片、视频和音频的标签页切换
- 为打卡卡片添加文本内容折叠/展开功能,当文本溢出时显示"全文/收起"按钮
- 重构媒体展示逻辑,当存在多种媒体类型时使用标签页组织,单一类型时保持原有布局
- 添加单元测试确保组件功能正确性,配置Vitest测试环境
- 更新TypeScript类型定义和开发依赖以支持测试
...@@ -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 -->
......
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.