hookehuyr

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

- 在环境变量中启用多附件功能,支持图片、视频和音频的标签页切换
- 为打卡卡片添加文本内容折叠/展开功能,当文本溢出时显示"全文/收起"按钮
- 重构媒体展示逻辑,当存在多种媒体类型时使用标签页组织,单一类型时保持原有布局
- 添加单元测试确保组件功能正确性,配置Vitest测试环境
- 更新TypeScript类型定义和开发依赖以支持测试
......@@ -17,7 +17,7 @@ VITE_CONSOLE = 0
VITE_APPID=微信appID
# 是否开启多附件功能
VITE_CHECKIN_MULTI_ATTACHMENT = 0
VITE_CHECKIN_MULTI_ATTACHMENT = 1
# 是否开启打卡草稿缓存功能
VITE_CHECKIN_DRAFT_CACHE = 0
......
This diff could not be displayed because it is too large.
......@@ -58,9 +58,11 @@
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"@vitejs/plugin-vue-jsx": "^4.1.2",
"@vue/test-utils": "^2.4.6",
"@vueuse/core": "^13.0.0",
"autoprefixer": "^10.4.19",
"axios": "^1.8.4",
"jsdom": "^24.1.3",
"less": "^4.2.2",
"postcss": "^8.4.35",
"qs": "^6.14.0",
......
......@@ -71,6 +71,6 @@ declare global {
// for type re-export
declare global {
// @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}
......
This diff is collapsed. Click to expand it.
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import CheckinCard from '../CheckinCard.vue'
describe('CheckinCard.vue', () => {
const defaultPost = {
id: 1,
user: { name: 'Test User', avatar: '' },
content: 'Test Content',
images: [],
videoList: [],
audio: [],
likes: 0,
is_liked: false,
is_my: false
}
const globalStubs = {
'van-image': true,
'van-row': true,
'van-col': true,
'van-icon': true,
'van-image-preview': true,
'van-tabs': { template: '<div><slot /></div>' },
'van-tab': { name: 'van-tab', template: '<div><slot /></div>', props: ['title'] },
'PostCountModel': true,
'VideoPlayer': true,
'AudioPlayer': true
}
it('renders content correctly', () => {
const wrapper = mount(CheckinCard, {
props: { post: defaultPost },
global: {
stubs: globalStubs
}
})
expect(wrapper.text()).toContain('Test Content')
})
it('shows expand button when content overflows', async () => {
const wrapper = mount(CheckinCard, {
props: { post: { ...defaultPost, content: 'Long content...' } },
global: {
stubs: globalStubs
}
})
const textEl = wrapper.find('.post-text').element
Object.defineProperty(textEl, 'scrollHeight', { value: 200, configurable: true })
Object.defineProperty(textEl, 'clientHeight', { value: 100, configurable: true })
await wrapper.setProps({ post: { ...defaultPost, content: 'Updated Long Content' } })
await wrapper.vm.$nextTick()
})
it('shows tabs when multiple media types exist', () => {
const postWithMultiMedia = {
...defaultPost,
images: ['img1.jpg'],
videoList: [{ id: 1, video: 'vid1.mp4' }],
audio: []
}
const wrapper = mount(CheckinCard, {
props: { post: postWithMultiMedia },
global: {
stubs: globalStubs
}
})
const tabs = wrapper.findAllComponents({ name: 'van-tab' })
expect(tabs.length).toBe(2)
expect(tabs[0].props('title')).toBe('图片')
expect(tabs[1].props('title')).toBe('视频')
})
it('does not show tabs when single media type exists', () => {
const postWithSingleMedia = {
...defaultPost,
images: ['img1.jpg'],
videoList: [],
audio: []
}
const wrapper = mount(CheckinCard, {
props: { post: postWithSingleMedia },
global: {
stubs: globalStubs
}
})
const tabs = wrapper.findAllComponents({ name: 'van-tab' })
expect(tabs.length).toBe(0)
expect(wrapper.find('.post-images').exists()).toBe(true)
})
})
<!--
* @Date: 2025-05-29 15:34:17
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2026-01-24 15:29:28
* @LastEditTime: 2026-01-26 09:52:40
* @FilePath: /mlaj/src/views/checkin/IndexCheckInPage.vue
* @Description: 用户打卡主页
-->
......
/*
* @Date: 2026-01-26 13:31:38
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2026-01-26 13:33:54
* @FilePath: /mlaj/vitest.config.js
* @Description: 文件描述
*/
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
}
},
test: {
environment: 'jsdom',
css: {
include: [],
modules: {
classNameStrategy: 'non-scoped'
}
},
server: {
deps: {
inline: ['vant']
}
}
}
})
This diff could not be displayed because it is too large.