2025/2/8第一次更新

This commit is contained in:
爱吃咸鱼小猫咪
2025-02-08 18:50:38 +08:00
commit d7af560866
26519 changed files with 5046029 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
## 1.0.0
- 完成组件从主包的迁移
## 1.0.1
- 新增`briefOperation`插槽设置右边操作区域
- 优化内容展示样式
## 1.0.2
- 修复在三张图片的情况下,图片显示不正确的问题
- 新增`show-more`参数控制更多区域的显示与隐藏
## 1.0.5
- 优化样式
## 1.0.6
- 优化点击事件
## 1.0.8
- 新增`bottomRight`插槽
## 1.0.9
- 优化样式
## 1.0.10
- 修复在部分模式下点击图片无法预览的问题
## 1.0.11
- 修复类型引用出错的问题
## 1.0.13
- 优化样式
## 1.0.14
- 参数修改`view`相关参数修改为`hot`
## 1.0.15
- 修复`view-count`不显示的问题
+13
View File
@@ -0,0 +1,13 @@
Copyright (c) 2023-present Tuniao Technology
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+200
View File
@@ -0,0 +1,200 @@
# 图鸟 UI vue3 uniapp Plugins - 图文卡片
![TuniaoUI vue3 uniapp](https://resource.tuniaokj.com/images/vue3/market/vue3-banner-min.jpg 'TuniaoUI vue3 uniapp')
[Tuniao UI vue3官方仓库](https://github.com/tuniaoTech/tuniaoui-rc-vue3-uniapp)
该组件一般用于展示图文简要的信息,让用户可以快速查看筛选数据。
## 组件安装
```bash
npm install tnuiv3p-tn-graphic-card
```
## 组件位置
```typescript
import TnGraphicCard from 'tnuiv3p-tn-graphic-card/index.vue'
```
## 平台差异说明
| App(vue) | H5 | 微信小程序 | 支付宝小程序 | ... |
| :------: | :-: | :--------: | :----------: | :----: |
| √ | √ | √ | √ | 适配中 |
## 基础使用
- 通过`avatar`设置作者的头像
- 通过`title`设置标题
- 通过`description`设置描述
- 通过`tags`设置内容的标签,传递一个字符串数组
- 通过`content`设置文章的简要内容
- 通过`images`设置文章的图片
- 通过`viewCount`设置文章的浏览量
- 通过`commentCount`设置文章的评论量
- 通过`likeCount`设置文章的点赞量
- 通过`viewUserAvatars`设置浏览用户的头像,传递一个头像url地址的字符串数组
只有`avatar``title``description`为必填项,其他都为选填项
```vue
<script lang="ts" setup>
const graphicData = {
id: 1,
avatar: 'https://tnuiimage.tnkjapp.com/uniapp_market/circle/3-image.jpg',
title: '文章标题',
description: '2023年6月30日',
tags: ['标签1', '标签2'],
content:
'看看打开打开年份记得当年开始放假哦额外欺骗人u饿哦漂亮放佛怕打雷舒服呢我脾气破费了发多少呢;否,可能打飞机哦喷泉哦怕可能地方打开螺丝粉年份的肌肤看能否。',
images: [
'https://tnuiimage.tnkjapp.com/swiper/ad1.jpg',
'https://tnuiimage.tnkjapp.com/swiper/ad2.jpg',
'https://tnuiimage.tnkjapp.com/swiper/ad3.jpg',
'https://tnuiimage.tnkjapp.com/swiper/ad4.jpg',
],
viewCount: 100,
commentCount: 101,
likeCount: 102,
viewUserAvatars: [
'https://tnuiimage.tnkjapp.com/avatar/normal/1.png',
'https://tnuiimage.tnkjapp.com/avatar/normal/2.png',
'https://tnuiimage.tnkjapp.com/avatar/normal/3.png',
'https://tnuiimage.tnkjapp.com/avatar/normal/4.png',
'https://tnuiimage.tnkjapp.com/avatar/normal/5.png',
'https://tnuiimage.tnkjapp.com/avatar/normal/6.png',
'https://tnuiimage.tnkjapp.com/avatar/normal/7.png',
'https://tnuiimage.tnkjapp.com/avatar/normal/8.png',
],
}
</script>
<template>
<TnGraphicCard
:avatar="graphicData.avatar"
:title="graphicData.title"
:description="graphicData.description"
:tags="graphicData.tags"
:content="graphicData.content"
:images="graphicData.images"
:view-count="graphicData.viewCount"
:comment-count="graphicData.commentCount"
:like-count="graphicData.likeCount"
:view-user-avatars="graphicData.viewUserAvatars"
/>
</template>
```
## 设置查看、评论、点赞的状态
设置`show-view``show-comment``show-like`属性为`true`,则显示对应的状态,否则不显示
设置`active-show``active-comment``active-like`属性为`true`,则显示对应的状态为激活状态,否则为未激活状态
#### 隐藏查看数据并且设置为已点赞
```vue
<template>
<TnGraphicCard
:avatar="graphicData.avatar"
:title="graphicData.title"
:description="graphicData.description"
:tags="graphicData.tags"
:content="graphicData.content"
:images="graphicData.images"
:view-count="graphicData.viewCount"
:comment-count="graphicData.commentCount"
:like-count="graphicData.likeCount"
:view-user-avatars="graphicData.viewUserAvatars"
:show-view="false"
active-like
/>
</template>
```
## 设置显示最大显示浏览用户头像数量
设置`max-view-user-avatar-count`的值则当前显示的浏览用户头像数量不会超过该值
如果浏览用户数据为空或者设置了`show-view-user``false`则不显示浏览用户的头像信息
#### 设置最大显示浏览用户头像数量为3
```vue
<template>
<TnGraphicCard
:avatar="graphicData.avatar"
:title="graphicData.title"
:description="graphicData.description"
:tags="graphicData.tags"
:content="graphicData.content"
:images="graphicData.images"
:view-count="graphicData.viewCount"
:comment-count="graphicData.commentCount"
:like-count="graphicData.likeCount"
:view-user-avatars="graphicData.viewUserAvatars"
:max-view-user-avatar-count="3"
/>
</template>
```
## API
### Props
| 属性名 | 说明 | 类型 | 默认值 | 可选值 |
| -------------------------- | ------------------------------------------------------------ | -------- | ------------ | ------------------------------------------------------------ |
| avatar | 发布者头像地址 | String | - | - |
| title | 标题 | String | - | - |
| description | 描述 | String | - | - |
| content | 内容 | String | - | - |
| tags | 标签 | String[] | [] | - |
| images | 图片列表 | String[] | [] | - |
| tag-bg-color | 标签背景颜色,可以使用图鸟内置的[背景色](https://vue3.tuniaokj.com/zh-CN/guide/style/background.html)、hex、rgb、rgba | String | - | - |
| tag-text-color | 标签文字颜色,支持图鸟内置的[颜色值](https://vue3.tuniaokj.com/zh-CN/guide/style/text.html)、hex、rgb、rgba | String | - | - |
| show-more | 显示右边更多操作区域 | Boolean | `true` | `false` |
| show-hot | 显示热度数量 | Boolean | `true` | `false` |
| active-hot | 是否激活热度 | Boolean | `false` | `true` |
| hot-count | 热度数量数据 | Number | 0 | - |
| hot-icon | 热度数量图标 | String | rocket | [图标有效值](https://vue3.tuniaokj.com/zh-CN/component/icon.html) |
| active-hot-icon | 激活时热度数量图标 | String | rocket-fill | [图标有效值](https://vue3.tuniaokj.com/zh-CN/component/icon.html) |
| hot-color | 热度数量图标颜色,支持图鸟内置的[颜色值](https://vue3.tuniaokj.com/zh-CN/guide/style/text.html)、hex、rgb、rgba | String | - | - |
| active-hot-color | 激活时热度数量图标颜色,支持图鸟内置的[颜色值](https://vue3.tuniaokj.com/zh-CN/guide/style/text.html)、hex、rgb、rgba | String | - | - |
| show-comment | 显示评论数量 | Boolean | `true` | `false` |
| active-comment | 是否激活评论 | Boolean | `false` | `true` |
| comment-count | 评论数量数据 | Number | 0 | - |
| comment-icon | 评论数量图标 | String | comment | [图标有效值](https://vue3.tuniaokj.com/zh-CN/component/icon.html) |
| active-comment-icon | 激活时评论数量图标 | String | comment-fill | [图标有效值](https://vue3.tuniaokj.com/zh-CN/component/icon.html) |
| comment-color | 评论数量图标颜色,支持图鸟内置的[颜色值](https://vue3.tuniaokj.com/zh-CN/guide/style/text.html)、hex、rgb、rgba | String | - | - |
| active-comment-color | 激活时评论数量图标颜色,支持图鸟内置的[颜色值](https://vue3.tuniaokj.com/zh-CN/guide/style/text.html)、hex、rgb、rgba | String | - | - |
| show-like | 显示点赞数量 | Boolean | `true` | `false` |
| active-like | 是否激活点赞 | Boolean | `false` | `true` |
| like-count | 点赞数量数据 | Number | 0 | - |
| like-icon | 点赞数量图标 | String | like | [图标有效值](https://vue3.tuniaokj.com/zh-CN/component/icon.html) |
| active-like-icon | 激活时点赞数量图标 | String | like-fill | [图标有效值](https://vue3.tuniaokj.com/zh-CN/component/icon.html) |
| like-color | 点赞数量图标颜色,支持图鸟内置的[颜色值](https://vue3.tuniaokj.com/zh-CN/guide/style/text.html)、hex、rgb、rgba | String | - | - |
| active-like-color | 激活时点赞数量图标颜色,支持图鸟内置的[颜色值](https://vue3.tuniaokj.com/zh-CN/guide/style/text.html)、hex、rgb、rgba | String | - | - |
| show-view-user | 显示查看用户头像信息 | Boolean | `true` | `false` |
| view-count | 实际查看数量数据 | Number | 0 | - |
| view-user-avatars | 查看用户头像列表 | String[] | [] | - |
| max-view-user-avatar-count | 最大显示用户头像数量 | Number | 4 | - |
### Slots
| 插槽名 | 说明 |
| -------------- | ---------------- |
| briefOperation | 顶部右边操作区域 |
| bottomRight | 底部右边操作区域 |
### Events
| 事件名 | 说明 | 类型 |
| ----------------- | ---------------------------- | ------------ |
| click | 图文卡片点击事件 | `() => void` |
| avatar-view-click | 发布者头像和查看数量点击事件 | `() => void` |
| more-click | 更多按钮点击事件 | `() => void` |
| hot-click | 热度数量点击事件 | `() => void` |
| comment-click | 评论数量点击事件 | `() => void` |
| like-click | 点赞数量点击事件 | `() => void` |
+169
View File
@@ -0,0 +1,169 @@
import { computed, toRef } from 'vue'
import { useComponentColor, useNamespace } from '@tuniao/tnui-vue3-uniapp/hooks'
import type { CSSProperties } from 'vue'
import type { GraphicCardProps } from '../types'
export const useGraphicCardCustomStyle = (props: GraphicCardProps) => {
const ns = useNamespace('graphic-card')
// 解析颜色
const [tagBgColorClass, tagBgColorStyle] = useComponentColor(
toRef(props, 'tagBgColor'),
'bg'
)
const [tagTextColorClass, tagTextColorStyle] = useComponentColor(
toRef(props, 'tagTextColor'),
'text'
)
const [hotColorClass, hotColorStyle] = useComponentColor(
toRef(props, 'hotColor'),
'text'
)
const [activeHotColorClass, activeHotColorStyle] = useComponentColor(
toRef(props, 'activeHotColor'),
'text'
)
const [commentColorClass, commentColorStyle] = useComponentColor(
toRef(props, 'commentColor'),
'text'
)
const [activeCommentColorClass, activeCommentColorStyle] = useComponentColor(
toRef(props, 'activeCommentColor'),
'text'
)
const [likeColorClass, likeColorStyle] = useComponentColor(
toRef(props, 'likeColor'),
'text'
)
const [activeLikeColorClass, activeLikeColorStyle] = useComponentColor(
toRef(props, 'activeLikeColor'),
'text'
)
// 标签对应的类
const tagClass = computed<string>(() => {
const cls: string[] = []
if (tagBgColorClass.value) cls.push(tagBgColorClass.value)
if (tagTextColorClass.value) cls.push(tagTextColorClass.value)
return cls.join(' ')
})
// 标签对应的样式
const tagStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (!tagBgColorClass.value) {
style.backgroundColor =
tagBgColorStyle.value || 'var(--tn-color-gray-disabled)'
}
if (tagTextColorStyle.value) {
style.color = tagTextColorStyle.value
} else if (!tagTextColorClass.value && !tagBgColorClass.value) {
style.color = 'var(--tn-text-color-primary)'
}
return style
})
// 热度对应的类
const hotClass = computed<string>(() => {
const cls: string[] = [ns.e('hot')]
if (props.activeHot) {
if (activeHotColorClass.value) cls.push(activeHotColorClass.value)
} else {
if (hotColorClass.value) cls.push(hotColorClass.value)
}
return cls.join(' ')
})
// 热度对应的样式
const hotStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (props.activeHot) {
if (!activeHotColorClass.value) {
style.color = activeHotColorStyle.value || 'var(--tn-color-primary)'
}
} else {
if (!hotColorClass.value) {
style.color = hotColorStyle.value || 'var(--tn-color-gray)'
}
}
return style
})
// 评论对应的类
const commentClass = computed<string>(() => {
const cls: string[] = [ns.e('comment')]
if (props.activeComment) {
if (activeCommentColorClass.value) cls.push(activeCommentColorClass.value)
} else {
if (commentColorClass.value) cls.push(commentColorClass.value)
}
return cls.join(' ')
})
// 评论对应的样式
const commentStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (props.activeComment) {
if (!activeCommentColorClass.value) {
style.color = activeCommentColorStyle.value || 'var(--tn-color-primary)'
}
} else {
if (!commentColorClass.value) {
style.color = commentColorStyle.value || 'var(--tn-color-gray)'
}
}
return style
})
// 点赞对应的类
const likeClass = computed<string>(() => {
const cls: string[] = [ns.e('like')]
if (props.activeLike) {
if (activeLikeColorClass.value) cls.push(activeLikeColorClass.value)
} else {
if (likeColorClass.value) cls.push(likeColorClass.value)
}
return cls.join(' ')
})
// 点赞对应的样式
const likeStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (props.activeLike) {
if (!activeLikeColorClass.value) {
style.color = activeLikeColorStyle.value || 'var(--tn-color-red)'
}
} else {
if (!likeColorClass.value) {
style.color = likeColorStyle.value || 'var(--tn-color-gray)'
}
}
return style
})
return {
ns,
tagClass,
tagStyle,
hotClass,
hotStyle,
commentClass,
commentStyle,
likeClass,
likeStyle,
}
}
+2
View File
@@ -0,0 +1,2 @@
export * from './graphic-card-custom'
export * from './use-graphic-card'
+78
View File
@@ -0,0 +1,78 @@
import { computed, ref } from 'vue'
import { isEmptyVariableInDefault } from '@tuniao/tnui-vue3-uniapp/utils'
import type { SetupContext } from 'vue'
import type { GraphicCardEmits, GraphicCardProps } from '../types'
export const useGraphicCard = (
props: GraphicCardProps,
emits: SetupContext<GraphicCardEmits>['emit']
) => {
// 分割显示的查看用户头像列表和头像数量
const viewUserAvatars = ref<string[]>([])
const viewUserCount = ref<number>(0)
if (props.showViewUser) {
viewUserAvatars.value = props.viewUserAvatars.slice(
0,
props.maxViewUserAvatarCount
)
viewUserCount.value = props.viewUserAvatars.length
}
// 图片数量
const imageCount = computed<number>(() =>
isEmptyVariableInDefault(props?.images?.length, 0)
)
// 预览图片
const previewImageHandle = (index: number) => {
uni.previewImage({
urls: props.images,
current: index,
})
}
// 卡片点击事件
const cardClickEvent = () => {
emits('click')
}
// 用户头像点击事件
const handleAvatarClick = () => {
emits('avatar-view-click')
}
// 更多按钮点击事件
const handleMoreClick = () => {
emits('more-click')
}
// 点击热度数量
const handleHotClick = () => {
emits('hot-click')
}
// 点击评论数量
const handleCommentClick = () => {
emits('comment-click')
}
// 点击点赞数量
const handleLikeClick = () => {
emits('like-click')
}
return {
viewUserAvatars,
viewUserCount,
imageCount,
previewImageHandle,
cardClickEvent,
handleAvatarClick,
handleMoreClick,
handleHotClick,
handleCommentClick,
handleLikeClick,
}
}
+8
View File
@@ -0,0 +1,8 @@
import { withNoopInstall } from '@tuniao/tnui-vue3-uniapp/utils'
import graphicCard from './index.vue'
export const TnGraphicCard = withNoopInstall(graphicCard)
export default TnGraphicCard
export * from './types'
export type { TnGraphicCardInstance } from './instance'
+198
View File
@@ -0,0 +1,198 @@
<script lang="ts" setup>
import TnIcon from '@tuniao/tnui-vue3-uniapp/components/icon/src/icon.vue'
import TnPhotoAlbum from '@tuniao/tnui-vue3-uniapp/components/photo-album/src/photo-album.vue'
import TnAvatar from '@tuniao/tnui-vue3-uniapp/components/avatar/src/avatar.vue'
import TnAvatarGroup from '@tuniao/tnui-vue3-uniapp/components/avatar/src/avatar-group.vue'
import TnLazyLoad from '@tuniao/tnui-vue3-uniapp/components/lazy-load/src/lazy-load.vue'
import { graphicCardEmits, graphicCardProps } from './types'
import { useGraphicCard, useGraphicCardCustomStyle } from './composables'
const props = defineProps(graphicCardProps)
const emits = defineEmits(graphicCardEmits)
const {
viewUserAvatars,
viewUserCount,
imageCount,
previewImageHandle,
cardClickEvent,
handleAvatarClick,
handleMoreClick,
handleCommentClick,
handleHotClick,
handleLikeClick,
} = useGraphicCard(props, emits)
const {
ns,
tagClass,
tagStyle,
hotClass,
hotStyle,
commentClass,
commentStyle,
likeClass,
likeStyle,
} = useGraphicCardCustomStyle(props)
</script>
<template>
<view :class="[ns.b()]" @tap="cardClickEvent">
<!-- 简要信息 -->
<view :class="[ns.e('brief-info')]">
<view :class="[ns.e('brief-info__content')]">
<view
:class="[ns.e('brief-info__avatar')]"
@tap.stop="handleAvatarClick"
>
<image class="image" :src="avatar" mode="aspectFill" />
</view>
<view :class="[ns.e('brief-info__data')]">
<view class="title tn-text-ellipsis-1">{{ title }}</view>
<view v-if="description" class="desc tn-text-ellipsis-1">
{{ description }}
</view>
</view>
</view>
<view v-if="showMore" :class="[ns.e('brief-info__operation')]">
<slot name="briefOperation">
<view
:class="[ns.em('brief-info__operation', 'more')]"
@tap.stop="handleMoreClick"
>
<TnIcon name="more-vertical" />
</view>
</slot>
</view>
</view>
<!-- 内容容器 -->
<view :class="[ns.e('container')]">
<!-- 内容 -->
<view :class="[ns.e('content')]">
<!-- 标签和内容 -->
<view :class="[ns.e('content__tags')]">
<view
v-for="(tagItem, tagIndex) in tags"
:key="tagIndex"
class="tag-item"
:class="[tagClass]"
:style="tagStyle"
>
<TnIcon name="topics-fill" />
{{ tagItem }}
</view>
</view>
<view :class="[ns.e('content__data')]">
{{ content }}
</view>
</view>
<!-- 图片列表 -->
<view v-if="!!imageCount" :class="[ns.e('images')]">
<!-- 一张图片 -->
<view
v-if="imageCount === 1"
:class="[ns.em('images', 'item'), ns.is('one')]"
@tap.stop="previewImageHandle(0)"
>
<TnLazyLoad :src="images[0]" />
</view>
<!-- 两张图片 -->
<view
v-if="imageCount === 2"
:class="[ns.em('images', 'item'), ns.is('two')]"
>
<TnPhotoAlbum :data="images" :column="2" />
</view>
<!-- 三张图片 -->
<view
v-if="imageCount === 3"
:class="[ns.em('images', 'item'), ns.is('three')]"
>
<view class="image-wrapper-left">
<view class="image-container" @tap.stop="previewImageHandle(0)">
<TnLazyLoad :src="images[0]" />
</view>
</view>
<view class="image-wrapper-right">
<view class="image-container" @tap.stop="previewImageHandle(1)">
<TnLazyLoad :src="images[1]" />
</view>
<view class="image-container" @tap.stop="previewImageHandle(2)">
<TnLazyLoad :src="images[2]" />
</view>
</view>
</view>
<!-- 四张图片 -->
<view
v-if="imageCount === 4"
:class="[ns.em('images', 'item'), ns.is('four')]"
>
<TnPhotoAlbum :data="images" :column="2" />
</view>
<TnPhotoAlbum v-if="imageCount >= 5" :data="images" />
</view>
</view>
<!-- 底部信息 -->
<view
:class="[ns.e('bottom-info'), ns.is('no-content', !!$slots.bottomRight)]"
>
<view :class="[ns.e('bottom-info__left')]">
<view
v-if="showHot"
class="count-item-data"
:class="[hotClass]"
:style="hotStyle"
@tap.stop="handleHotClick"
>
<TnIcon :name="activeHot ? activeHotIcon : hotIcon" />
<view class="count">{{ hotCount }}</view>
</view>
<view
v-if="showComment"
class="count-item-data"
:class="[commentClass]"
:style="commentStyle"
@tap.stop="handleCommentClick"
>
<TnIcon :name="activeComment ? activeCommentIcon : commentIcon" />
<view class="count">{{ commentCount }}</view>
</view>
<view
v-if="showLike"
class="count-item-data"
:class="[likeClass]"
:style="likeStyle"
@tap.stop="handleLikeClick"
>
<TnIcon :name="activeLike ? activeLikeIcon : likeIcon" />
<view class="count">{{ likeCount }}</view>
</view>
</view>
<view
v-if="(showViewUser && viewUserAvatars.length) || $slots.bottomRight"
:class="[ns.e('bottom-info__right')]"
>
<slot name="bottomRight">
<!-- 查看用户头像列表 -->
<view :class="[ns.e('view-user-list')]">
<TnAvatarGroup border size="sm">
<TnAvatar
v-for="(viewUserAvatar, viewUserIndex) in viewUserAvatars"
:key="viewUserIndex"
:url="viewUserAvatar"
/>
</TnAvatarGroup>
</view>
<!-- 查看用户数量 -->
<view :class="[ns.e('view-user-count')]">
{{ viewCount !== undefined ? viewCount : viewUserCount }}
</view>
</slot>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
@import './theme-chalk/index.scss';
</style>
+3
View File
@@ -0,0 +1,3 @@
import type GraphicCard from './index.vue'
export type TnGraphicCardInstance = InstanceType<typeof GraphicCard>
+34
View File
@@ -0,0 +1,34 @@
{
"name": "tnuiv3p-tn-graphic-card",
"version": "1.0.15",
"description": "TuniaoUI vue3 uniapp 插件",
"keywords": [
"tn",
"tuniao",
"tuniao-ui",
"图鸟",
"uniapp",
"uniapp插件"
],
"license": "Apache 2.0",
"author": "Tuniao Technology Co., Ltd.",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/tuniaoTech/tnuiv3p-graphic-card"
},
"bugs": {
"url": "https://github.com/tuniaoTech/tnuiv3p-graphic-card/issues"
},
"peerDependencies": {
"@tuniao/tn-icon": "^1.4.0",
"@tuniao/tn-style": "^1.0.17",
"@tuniao/tnui-vue3-uniapp": "^1.0.5",
"sass": "^1.61.0",
"typescript": "^5.1.6",
"vue": "^3.2.47"
},
"gitHead": ""
}
+215
View File
@@ -0,0 +1,215 @@
@use './mixins/mixins.scss' as *;
@include b(graphic-card) {
position: relative;
width: 100%;
padding: 30rpx;
background-color: var(--tn-color-white);
/* 简要信息 start */
@include e(brief-info) {
display: flex;
align-items: center;
/* 简要内容 start */
&__content {
flex-grow: 1;
display: flex;
}
&__avatar {
flex-shrink: 0;
width: 90rpx;
height: 90rpx;
border-radius: 50%;
background-color: var(--tn-color-gray--disabled);
.image {
width: 100%;
height: 100%;
border-radius: inherit;
}
}
&__data {
flex-grow: 1;
margin-left: 24rpx;
color: var(--tn-text-color-primary);
line-height: 1;
padding-top: 6rpx;
.title {
font-size: 30rpx;
}
.desc {
margin-top: 16rpx;
font-size: 24rpx;
color: var(--tn-color-gray);
}
}
/* 简要内容 end */
/* 操作按钮 start */
&__operation {
flex-grow: 0;
&--more {
font-size: 40rpx;
font-weight: bold;
color: var(--tn-color-gray-disabled);
padding-left: 20rpx;
}
}
/* 操作按钮 end */
}
/* 简要信息 end */
/* 内容 start */
@include e(container) {
margin-top: 20rpx;
}
@include e(content) {
/* 内容标签 start */
&__tags {
display: flex;
float: left;
align-items: center;
margin-top: 4rpx;
.tag-item {
width: fit-content;
height: fit-content;
padding: 8rpx 16rpx;
border-radius: 6rpx;
margin-right: 16rpx;
font-size: 24rpx;
line-height: 1;
}
}
/* 内容标签 end */
/* 内容数据 start */
&__data {
font-size: 28rpx;
line-height: 1.8em;
// display: inline-block;
color: var(--tn-text-color-primary);
// 文字两端对齐
text-align: justify;
}
/* 内容数据 end */
}
/* 图片列表 start */
@include e(images) {
width: 100%;
margin-top: 20rpx;
@include m(item) {
/* 一张图片 start */
@include when(one) {
width: 70%;
height: 300rpx;
border-radius: 15rpx;
}
/* 一张图片 end */
/* 两张图片 start */
@include when(two) {
width: 80%;
}
/* 两张图片 end */
/* 三张图片 start */
@include when(three) {
position: relative;
width: 100%;
height: 332rpx;
display: flex;
align-items: center;
/* 左边单图 start */
.image-wrapper-left {
position: relative;
width: 50%;
height: 100%;
.image-container {
width: 100%;
height: 100%;
border-radius: 15rpx;
}
}
/* 左边单图 end */
/* 右边双图 start */
.image-wrapper-right {
position: relative;
flex: 1;
height: 100%;
margin-left: 28rpx;
display: flex;
flex-direction: column;
align-items: center;
.image-container {
flex: 1;
width: 100%;
border-radius: 15rpx;
& + .image-container {
margin-top: 28rpx;
}
}
}
/* 右边双图 end */
}
/* 三张图片 end */
/* 四张图片 start */
@include when(four) {
width: 80%;
}
/* 四张图片 end */
}
}
/* 图片列表 end */
/* 内容 end */
/* 底部信息 start */
@include e(bottom-info) {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 16rpx;
/* 左边查看数据 start */
&__left {
display: flex;
align-items: center;
.count-item-data {
display: flex;
align-items: center;
margin-right: 20rpx;
.count {
margin-left: 6rpx;
}
}
}
/* 左边查看数据 end */
/* 右边查看用户头像数据 start */
&__right {
display: flex;
align-items: center;
}
/* 右边查看用户头像数据 end */
@include when(no-content) {
margin-top: 24rpx;
}
}
@include e(view-user-count) {
margin-left: 8rpx;
font-size: 26rpx;
color: var(--tn-color-gray);
}
/* 底部信息 end */
}
+5
View File
@@ -0,0 +1,5 @@
$namespace: 'tn' !default;
$common-separator: '-' !default;
$element-separator: '__' !default;
$modifier-separator: '--' !default;
$state-prefix: 'is-' !default;
+105
View File
@@ -0,0 +1,105 @@
@use 'config';
// BEM support Func
// 块(Block):代表一个独立的组件或页面中的一个大型部分。块可以看作是一个命名空间,用于包含相关元素和修饰符。例如,一个导航栏可以被视为一个块。
// 元素(Element):代表块的一部分,但不能独立存在。元素总是属于一个块,并且与该块紧密相关。元素由块名称和元素名称组成,中间由双下划线(__)连接。例如,一个导航栏可以包含多个链接,链接可以被视为导航栏块的元素。
// 修饰符(Modifier):代表块或元素的变体或状态。修饰符用于修改块或元素的外观或行为。修饰符由块名称或元素名称,连字符(-)和修饰符名称组成。例如,一个导航栏可以具有活动状态或浅色主题,这些状态可以通过添加修饰符类进行实现。
@function selectorToString($selector) {
$selector: inspect($selector);
$selector: str-slice($selector, 2, -2);
@return $selector;
}
@function containsModifier($selector) {
$selector: selectorToString($selector);
@if str_index($selector, config.$modifier-separator) {
@return true;
} @else {
@return false;
}
}
@function containWhenFlag($selector) {
$selector: selectorToString($selector);
@if str-index($selector, '.' + config.$state-prefix) {
@return true;
} @else {
@return false;
}
}
@function containPseudoClass($selector) {
$selector: selectorToString($selector);
@if str-index($selector, ':') {
@return true;
} @else {
@return false;
}
}
@function hitAllSpecialNestRule($selector) {
@return containsModifier($selector) or containWhenFlag($selector) or
containPseudoClass($selector);
}
// join var name
// joinVarName(('button', 'text-color')) => '--tn-button-text-color'
@function joinVarName($list) {
$name: '--' + config.$namespace;
@each $item in $list {
@if $item != '' {
$name: $name + '-' + $item;
}
}
@return $name;
}
// getCssVarName('button', 'text-color') => '--tn-button-text-color'
@function getCssVarName($args...) {
@return joinVarName($args);
}
// getCssVar('button', 'text-color') => var(--tn-button-text-color)
@function getCssVar($args...) {
@return var(#{joinVarName($args)});
}
// getCssVarWithDefault('button', 'text-color', 'red') => var(--tn-button-text-color, red)
@function getCssVarWithDefault($args, $default) {
@return var(#{joinVarName($args)}, #{$default});
}
// bem('block', 'element', ''modifier) => 'tn-block__element--modifier'
@function bem($block, $element: '', $modifier: '') {
$name: config.$namespace + config.$common-separator + $block;
@if $element != '' {
$name: $name + config.$element-separator + $element;
}
@if $modifier != '' {
$name: $name + config.$modifier-separator + $modifier;
}
@return $name;
}
// 字符串替换
@function str-replace($string, $search, $replace) {
$index: str-index($string, $search);
@if $index {
@return str-slice($string, 1, $index - 1) + $replace +
str-replace(
str-slice($string, $index + str-length($search)),
$search,
$replace
);
}
@return $string;
}
+134
View File
@@ -0,0 +1,134 @@
@use 'function' as *;
// forward mixins
@forward 'config';
@forward 'function';
@use 'config' as *;
// BEM
@mixin b($block) {
$B: $namespace + '-' + $block !global;
.#{$B} {
@content;
}
}
@mixin e($element) {
$E: $element !global;
$selector: &;
$currentSelector: '';
@each $unit in $element {
$currentSelector: #{$currentSelector +
'.' +
$B +
$element-separator +
$unit +
','};
}
@if hitAllSpecialNestRule($selector) {
@at-root {
#{$selector} {
#{$currentSelector} {
@content;
}
}
}
} @else {
@at-root {
#{$currentSelector} {
@content;
}
}
}
}
@mixin m($modifier) {
$selector: &;
$currentSelector: '';
@each $unit in $modifier {
$currentSelector: #{$currentSelector +
$selector +
$modifier-separator +
$unit +
','};
}
@at-root {
#{$currentSelector} {
@content;
}
}
}
@mixin configurable-m($modifier, $E-flag: false) {
$selector: &;
$interpolation: '';
@if $E-flag {
$interpolation: $element-separator + $E-flag;
}
@at-root {
#{$selector} {
.#{$B + $interpolation + $modifier-separator + $modifier} {
@content;
}
}
}
}
@mixin spec-selector(
$specSelector: '',
$element: $E,
$modifier: false,
$block: $B
) {
$modifierCombo: '';
$elementCombo: '';
@if $modifier {
$modifierCombo: $modifier-separator + $modifier;
}
@if $element {
$elementCombo: $element-separator + $element;
}
@at-root {
#{&}#{$specSelector}.#{$block + $elementCombo + $modifierCombo} {
@content;
}
}
}
@mixin meb($modifier: false, $element: $E, $block: $B) {
$selector: &;
$modifierCombo: '';
@if $modifier {
$modifierCombo: $modifier-separator + $modifier;
}
@at-root {
#{$selector} {
.#{$block + $element-separator + $element + $modifierCombo} {
@content;
}
}
}
}
@mixin when($state) {
@at-root {
&.#{$state-prefix + $state} {
@content;
}
}
}
@mixin pseudo($pseudo) {
@at-root #{&}#{':#{$pseudo}'} {
@content;
}
}
+238
View File
@@ -0,0 +1,238 @@
import { buildProps, definePropType } from '@tuniao/tnui-vue3-uniapp/utils'
import type { ExtractPropTypes } from 'vue'
export const graphicCardProps = buildProps({
/**
* @description 头像地址
*/
avatar: {
type: String,
required: true,
},
/**
* @description 标题
*/
title: {
type: String,
required: true,
},
/**
* @description 描述
*/
description: {
type: String,
required: true,
},
/**
* @description 标签
*/
tags: {
type: definePropType<string[]>(Array),
default: () => [],
},
/**
* @description 标签背景颜色,以tn开头使用图鸟内置的颜色
*/
tagBgColor: String,
/**
* @description 标签文字颜色,以tn开头使用图鸟内置的颜色
*/
tagTextColor: String,
/**
* @description 内容
*/
content: String,
/**
* @description 图片列表
*/
images: {
type: definePropType<string[]>(Array),
default: () => [],
},
/**
* @description 是否显示更多(是否显示顶部右边操作区域)
*/
showMore: {
type: Boolean,
default: true,
},
/**
* @description 显示热度数量
*/
showHot: {
type: Boolean,
default: true,
},
/**
* @description 是否激活热度
*/
activeHot: Boolean,
/**
* @description 热度数量数据
*/
hotCount: {
type: Number,
default: 0,
},
/**
* @description 热度数量图标
*/
hotIcon: {
type: String,
default: 'rocket',
},
/**
* @description 激活时热度数量图标
*/
activeHotIcon: {
type: String,
default: 'rocket-fill',
},
/**
* @description 热度数量图标颜色
*/
hotColor: String,
/**
* @description 激活时热度数量图标颜色
*/
activeHotColor: String,
/**
* @description 显示评论数量
*/
showComment: {
type: Boolean,
default: true,
},
/**
* @description 是否激活评论
*/
activeComment: Boolean,
/**
* @description 评论数量数据
*/
commentCount: {
type: Number,
default: 0,
},
/**
* @description 评论数量图标
*/
commentIcon: {
type: String,
default: 'message',
},
/**
* @description 激活时评论数量图标
*/
activeCommentIcon: {
type: String,
default: 'message-fill',
},
/**
* @description 评论数量图标颜色
*/
commentColor: String,
/**
* @description 激活时评论数量图标颜色
*/
activeCommentColor: String,
/**
* @description 显示点赞数量
*/
showLike: {
type: Boolean,
default: true,
},
/**
* @description 是否激活点赞
*/
activeLike: Boolean,
/**
* @description 点赞数量数据
*/
likeCount: {
type: Number,
default: 0,
},
/**
* @description 点赞数量图标
*/
likeIcon: {
type: String,
default: 'like-lack',
},
/**
* @description 激活时点赞数量图标
*/
activeLikeIcon: {
type: String,
default: 'like-fill',
},
/**
* @description 点赞数量图标颜色
*/
likeColor: String,
/**
* @description 激活时点赞数量图标颜色
*/
activeLikeColor: String,
/**
* @description 显示查看用户信息
*/
showViewUser: {
type: Boolean,
default: true,
},
/**
* @description 实际查看数量数据
*/
viewCount: {
type: Number,
default: 0,
},
/**
* @description 查看用户头像列表
*/
viewUserAvatars: {
type: definePropType<string[]>(Array),
default: () => [],
},
/**
* @description 最大显示用户头像数量
*/
maxViewUserAvatarCount: {
type: Number,
default: 4,
},
})
export const graphicCardEmits = {
/**
* @description 点击图文卡片
*/
click: () => true,
/**
* @description 点击用户头像和浏览数量
*/
'avatar-view-click': () => true,
/**
* @description 更多按钮点击
*/
'more-click': () => true,
/**
* @description 点击热度数量
*/
'hot-click': () => true,
/**
* @description 点击评论数量
*/
'comment-click': () => true,
/**
* @description 点击点赞数量
*/
'like-click': () => true,
}
export type GraphicCardProps = ExtractPropTypes<typeof graphicCardProps>
export type GraphicCardEmits = typeof graphicCardEmits