fix: 冲突

This commit is contained in:
hw
2025-11-14 11:18:50 +08:00
526 changed files with 42388 additions and 3716 deletions

View File

@@ -1 +1 @@
export { default as ProductCategorySelect } from './category-select.vue';
export { default as ProductCategorySelect } from './select.vue';

View File

@@ -1,5 +1,4 @@
<script lang="ts" setup>
// TODO @AI一些 modal 是否使用 Modal 组件,而不是 el-modal
import { ref, watch } from 'vue';
import { ElButton, ElInput } from 'element-plus';

View File

@@ -27,7 +27,7 @@ const props = defineProps<{ modelValue: ComponentStyle }>();
const emit = defineEmits(['update:modelValue']);
const formData = useVModel(props, 'modelValue', emit);
const treeData = [
const treeData: any[] = [
{
label: '外部边距',
prop: 'margin',
@@ -96,7 +96,7 @@ const treeData = [
},
];
const handleSliderChange = (prop: string) => {
function handleSliderChange(prop: string) {
switch (prop) {
case 'borderRadius': {
formData.value.borderTopLeftRadius = formData.value.borderRadius;
@@ -120,7 +120,7 @@ const handleSliderChange = (prop: string) => {
break;
}
}
};
}
</script>
<template>

View File

@@ -53,11 +53,11 @@ watch(
);
/** 克隆组件 */
const handleCloneComponent = (component: DiyComponent<any>) => {
function handleCloneComponent(component: DiyComponent<any>) {
const instance = cloneDeep(component);
instance.uid = Date.now();
return instance;
};
}
</script>
<template>

View File

@@ -24,7 +24,7 @@ import { AppLinkInput, Draggable } from '#/views/mall/promotion/components';
import ComponentContainerProperty from '../../component-container-property.vue';
// 轮播图属性面板
/** 轮播图属性面板 */
defineOptions({ name: 'CarouselProperty' });
const props = defineProps<{ modelValue: CarouselProperty }>();

View File

@@ -7,7 +7,7 @@ import { onMounted, ref, watch } from 'vue';
import { ElScrollbar } from 'element-plus';
import * as CouponTemplateApi from '#/api/mall/promotion/coupon/couponTemplate';
import { getCouponTemplateList } from '#/api/mall/promotion/coupon/couponTemplate';
import {
CouponDiscount,
@@ -32,9 +32,7 @@ watch(
() => props.property.couponIds,
async () => {
if (props.property.couponIds?.length > 0) {
couponList.value = await CouponTemplateApi.getCouponTemplateList(
props.property.couponIds,
);
couponList.value = await getCouponTemplateList(props.property.couponIds);
}
},
{

View File

@@ -39,7 +39,7 @@ function handleToggleFab() {
<ElImage :src="item.imgUrl" fit="contain" class="h-7 w-7">
<template #error>
<div class="flex h-full w-full items-center justify-center">
<IconifyIcon icon="ep:picture" :color="item.textColor" />
<IconifyIcon icon="lucide:image" :color="item.textColor" />
</div>
</template>
</ElImage>
@@ -55,7 +55,7 @@ function handleToggleFab() {
<!-- todo: @owen 使用APP主题色 -->
<ElButton type="primary" size="large" circle @click="handleToggleFab">
<IconifyIcon
icon="ep:plus"
icon="lucide:plus"
class="transition-transform duration-300"
:class="expanded ? 'rotate-[135deg]' : 'rotate-0'"
/>

View File

@@ -23,9 +23,9 @@ const formData = useVModel(props, 'modelValue', emit);
const editDialogRef = ref(); // 热区编辑对话框
/** 打开热区编辑对话框 */
const handleOpenEditDialog = () => {
function handleOpenEditDialog() {
editDialogRef.value.open();
};
}
</script>
<template>

View File

@@ -11,7 +11,7 @@ export interface ImageBarProperty {
export const component = {
id: 'ImageBar',
name: '图片展示',
icon: 'ep:picture',
icon: 'lucide:image',
property: {
imgUrl: '',
url: '',

View File

@@ -13,7 +13,9 @@ import ComponentContainerProperty from '../../component-container-property.vue';
defineOptions({ name: 'ImageBarProperty' });
const props = defineProps<{ modelValue: ImageBarProperty }>();
const emit = defineEmits(['update:modelValue']);
const formData = useVModel(props, 'modelValue', emit);
</script>

View File

@@ -1,32 +1,25 @@
import { defineAsyncComponent } from 'vue';
/*
/**
* 组件注册
*
* 组件规范:
* 1. 每个子目录就是一个独立的组件,每个目录包括以下三个文件:
* 2. config.ts组件配置必选用于定义组件、组件默认的属性、定义属性的类型
* 3. index.vue组件展示用于展示组件的渲染效果。可以不提供如 Page页面设置只需要属性配置表单即可
* 4. property.vue组件属性表单用于配置组件必选
* 组件规范:每个子目录就是一个独立的组件,每个目录包括以下三个文件:
* 1. config.ts组件配置必选用于定义组件、组件默认的属性、定义属性的类型
* 2. index.vue组件展示用于展示组件的渲染效果。可以不提供如 Page页面设置只需要属性配置表单即可
* 3. property.vue组件属性表单用于配置组件必选
*
* 注:
* 组件IDconfig.ts中配置的id为准与组件目录的名称无关但还是建议组件目录的名称与组件ID保持一致
* 组件 IDconfig.ts 中配置的 id 为准,与组件目录的名称无关,但还是建议组件目录的名称与组件 ID 保持一致
*/
import { defineAsyncComponent } from 'vue';
// 导入组件界面模块
const viewModules: Record<string, any> = import.meta.glob('./*/*.vue');
// 导入配置模块
const viewModules: Record<string, any> = import.meta.glob('./*/*.vue'); // 导入组件界面模块
const configModules: Record<string, any> = import.meta.glob('./*/config.ts', {
eager: true,
});
}); // 导入配置模块
// 界面模块
const components: Record<string, any> = {};
// 组件配置模块
const componentConfigs: Record<string, any> = {};
const components: Record<string, any> = {}; // 界面模块
const componentConfigs: Record<string, any> = {}; // 组件配置模块
// 组件界面的类型
type ViewType = 'index' | 'property';
type ViewType = 'index' | 'property'; // 组件界面的类型
/**
* 注册组件的界面模块

View File

@@ -19,7 +19,7 @@ export interface NoticeContentProperty {
export const component = {
id: 'NoticeBar',
name: '公告栏',
icon: 'ep:bell',
icon: 'lucide:bell',
property: {
iconUrl: 'http://mall.yudao.iocoder.cn/static/images/xinjian.png',
contents: [

View File

@@ -11,7 +11,7 @@ export interface PageConfigProperty {
export const component = {
id: 'PageConfig',
name: '页面设置',
icon: 'ep:document',
icon: 'lucide:file-text',
property: {
description: '',
backgroundColor: '#f5f5f5',

View File

@@ -19,7 +19,6 @@ function handleActive(index: number) {
activeIndex.value = index;
}
</script>
<template>
<div
v-for="(item, index) in props.property.list"
@@ -35,7 +34,7 @@ function handleActive(index: number) {
<ElImage :src="item.imgUrl" fit="contain" class="h-full w-full">
<template #error>
<div class="flex h-full w-full items-center justify-center">
<IconifyIcon icon="ep:picture" />
<IconifyIcon icon="lucide:image" />
</div>
</template>
</ElImage>

View File

@@ -39,7 +39,7 @@ export interface ProductCardFieldProperty {
export const component = {
id: 'ProductCard',
name: '商品卡片',
icon: 'fluent:text-column-two-left-24-filled',
icon: 'lucide:grid-3x3',
property: {
layoutType: 'oneColBigImg',
fields: {

View File

@@ -9,19 +9,19 @@ import { fenToYuan } from '@vben/utils';
import { ElImage } from 'element-plus';
import * as ProductSpuApi from '#/api/mall/product/spu';
import { getSpuDetailList } from '#/api/mall/product/spu';
/** 商品卡片 */
defineOptions({ name: 'ProductCard' });
const props = defineProps<{ property: ProductCardProperty }>();
const spuList = ref<MallSpuApi.Spu[]>([]);
const spuList = ref<MallSpuApi.Spu[]>([]); // 商品列表
watch(
() => props.property.spuIds,
async () => {
spuList.value = await ProductSpuApi.getSpuDetailList(props.property.spuIds);
spuList.value = await getSpuDetailList(props.property.spuIds);
},
{
immediate: true,
@@ -37,17 +37,17 @@ function calculateSpace(index: number) {
return { marginLeft, marginTop };
}
const containerRef = ref();
const containerRef = ref(); // 容器
/** 计算商品的宽度 */
const calculateWidth = () => {
function calculateWidth() {
let width = '100%';
if (props.property.layoutType === 'twoCol') {
// 双列时每列的宽度为:(总宽度 - 间距)/ 2
width = `${(containerRef.value.offsetWidth - props.property.space) / 2}px`;
}
return { width };
};
}
</script>
<template>
<div

View File

@@ -9,7 +9,7 @@ import { fenToYuan } from '@vben/utils';
import { ElImage, ElScrollbar } from 'element-plus';
import * as ProductSpuApi from '#/api/mall/product/spu';
import { getSpuDetailList } from '#/api/mall/product/spu';
/** 商品栏 */
defineOptions({ name: 'ProductList' });
@@ -21,7 +21,7 @@ const spuList = ref<MallSpuApi.Spu[]>([]);
watch(
() => props.property.spuIds,
async () => {
spuList.value = await ProductSpuApi.getSpuDetailList(props.property.spuIds);
spuList.value = await getSpuDetailList(props.property.spuIds);
},
{
immediate: true,

View File

@@ -5,7 +5,7 @@ import type { MallArticleApi } from '#/api/mall/promotion/article';
import { ref, watch } from 'vue';
import * as ArticleApi from '#/api/mall/promotion/article/index';
import { getArticle } from '#/api/mall/promotion/article';
/** 营销文章 */
defineOptions({ name: 'PromotionArticle' });
@@ -18,7 +18,7 @@ watch(
() => props.property.id,
async () => {
if (props.property.id) {
article.value = await ArticleApi.getArticle(props.property.id);
article.value = await getArticle(props.property.id);
}
},
{

View File

@@ -10,8 +10,8 @@ import { fenToYuan } from '@vben/utils';
import { ElImage } from 'element-plus';
import * as ProductSpuApi from '#/api/mall/product/spu';
import * as CombinationActivityApi from '#/api/mall/promotion/combination/combinationActivity';
import { getSpuDetailList } from '#/api/mall/product/spu';
import { getCombinationActivityListByIds } from '#/api/mall/promotion/combination/combinationActivity';
/** 拼团卡片 */
defineOptions({ name: 'PromotionCombination' });
@@ -34,9 +34,7 @@ watch(
if (Array.isArray(activityIds) && activityIds.length > 0) {
// 获取拼团活动详情列表
combinationActivityList.value =
await CombinationActivityApi.getCombinationActivityListByIds(
activityIds,
);
await getCombinationActivityListByIds(activityIds);
// 获取拼团活动的 SPU 详情列表
spuList.value = [];
@@ -44,7 +42,7 @@ watch(
.map((activity) => activity.spuId)
.filter((spuId): spuId is number => typeof spuId === 'number');
if (spuIdList.value.length > 0) {
spuList.value = await ProductSpuApi.getSpuDetailList(spuIdList.value);
spuList.value = await getSpuDetailList(spuIdList.value);
}
// 更新 SPU 的最低价格
@@ -78,7 +76,7 @@ function calculateSpace(index: number) {
return { marginLeft, marginTop };
}
const containerRef = ref();
const containerRef = ref(); // 容器
/** 计算商品的宽度 */
function calculateWidth() {

View File

@@ -74,7 +74,7 @@ function calculateSpace(index: number) {
return { marginLeft, marginTop };
}
const containerRef = ref();
const containerRef = ref(); // 容器
/** 计算商品的宽度 */
function calculateWidth() {

View File

@@ -54,11 +54,6 @@ const formData = useVModel(props, 'modelValue', emit);
<IconifyIcon icon="fluent:text-column-two-24-filled" />
</ElRadioButton>
</ElTooltip>
<!--<ElTooltip class="item" content="三列" placement="bottom">
<ElRadioButton value="threeCol">
<IconifyIcon icon="fluent:text-column-three-24-filled" />
</ElRadioButton>
</ElTooltip>-->
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="商品名称" prop="fields.name.show">

View File

@@ -10,8 +10,8 @@ import { fenToYuan } from '@vben/utils';
import { ElImage } from 'element-plus';
import * as ProductSpuApi from '#/api/mall/product/spu';
import * as SeckillActivityApi from '#/api/mall/promotion/seckill/seckillActivity';
import { getSpuDetailList } from '#/api/mall/product/spu';
import { getSeckillActivityListByIds } from '#/api/mall/promotion/seckill/seckillActivity';
/** 秒杀卡片 */
defineOptions({ name: 'PromotionSeckill' });
@@ -31,7 +31,7 @@ watch(
if (Array.isArray(activityIds) && activityIds.length > 0) {
// 获取秒杀活动详情列表
seckillActivityList.value =
await SeckillActivityApi.getSeckillActivityListByIds(activityIds);
await getSeckillActivityListByIds(activityIds);
// 获取秒杀活动的 SPU 详情列表
spuList.value = [];
@@ -39,7 +39,7 @@ watch(
.map((activity) => activity.spuId)
.filter((spuId): spuId is number => typeof spuId === 'number');
if (spuIdList.value.length > 0) {
spuList.value = await ProductSpuApi.getSpuDetailList(spuIdList.value);
spuList.value = await getSpuDetailList(spuIdList.value);
}
// 更新 SPU 的最低价格
@@ -73,7 +73,7 @@ function calculateSpace(index: number) {
return { marginLeft, marginTop };
}
const containerRef = ref();
const containerRef = ref(); // 容器
/** 计算商品的宽度 */
function calculateWidth() {

View File

@@ -20,7 +20,7 @@ export type PlaceholderPosition = 'center' | 'left';
export const component = {
id: 'SearchBar',
name: '搜索框',
icon: 'ep:search',
icon: 'lucide:search',
property: {
height: 28,
showScan: false,

View File

@@ -30,7 +30,7 @@ defineProps<{ property: SearchProperty }>();
justifyContent: property.placeholderPosition,
}"
>
<IconifyIcon icon="ep:search" />
<IconifyIcon icon="lucide:search" />
<span>{{ property.placeholder || '搜索商品' }}</span>
</div>
<div class="absolute right-2 flex items-center justify-center gap-2">
@@ -39,10 +39,7 @@ defineProps<{ property: SearchProperty }>();
{{ keyword }}
</span>
<!-- 扫一扫 -->
<IconifyIcon
icon="ant-design:scan-outlined"
v-show="property.showScan"
/>
<IconifyIcon icon="lucide:scan-barcode" v-show="property.showScan" />
</div>
</div>
</div>

View File

@@ -5,7 +5,7 @@ import { IconifyIcon } from '@vben/icons';
import { ElImage } from 'element-plus';
/** 底部导航 */
/** 页面底部导航 */
defineOptions({ name: 'TabBar' });
defineProps<{ property: TabBarProperty }>();
@@ -26,7 +26,7 @@ defineProps<{ property: TabBarProperty }>();
<div
v-for="(item, index) in property.items"
:key="index"
class="tab-bar-item flex w-full flex-col items-center justify-center text-xs"
class="flex w-full flex-col items-center justify-center text-xs"
>
<ElImage
:src="index === 0 ? item.activeIconUrl : item.iconUrl"
@@ -35,7 +35,7 @@ defineProps<{ property: TabBarProperty }>();
<template #error>
<div class="flex h-full w-full items-center justify-center">
<IconifyIcon
icon="ep:picture"
icon="lucide:image"
class="h-[26px] w-[26px] rounded"
/>
</div>

View File

@@ -44,7 +44,7 @@ const handleThemeChange = () => {
</script>
<template>
<div class="tab-bar">
<div>
<ElForm :model="formData" label-width="80px">
<ElFormItem label="主题" prop="theme">
<ElSelect v-model="formData!.theme" @change="handleThemeChange">

View File

@@ -63,7 +63,10 @@ defineProps<{ property: TitleBarProperty }>();
<span v-if="property.more.type !== 'icon'">
{{ property.more.text }}
</span>
<IconifyIcon icon="ep:arrow-right" v-if="property.more.type !== 'text'" />
<IconifyIcon
icon="lucide:arrow-right"
v-if="property.more.type !== 'text'"
/>
</div>
</div>
</template>

View File

@@ -2,15 +2,14 @@ import type { ComponentStyle, DiyComponent } from '../../../util';
/** 用户卡券属性 */
export interface UserCouponProperty {
// 组件样式
style: ComponentStyle;
style: ComponentStyle; // 组件样式
}
// 定义组件
/** 定义组件 */
export const component = {
id: 'UserCoupon',
name: '用户卡券',
icon: 'ep:ticket',
icon: 'lucide:ticket',
property: {
style: {
bgType: 'color',

View File

@@ -9,7 +9,7 @@ export interface UserOrderProperty {
export const component = {
id: 'UserOrder',
name: '用户订单',
icon: 'ep:list',
icon: 'lucide:clipboard-list',
property: {
style: {
bgType: 'color',

View File

@@ -9,7 +9,7 @@ export interface UserWalletProperty {
export const component = {
id: 'UserWallet',
name: '用户资产',
icon: 'ep:wallet-filled',
icon: 'lucide:wallet',
property: {
style: {
bgType: 'color',

View File

@@ -17,7 +17,7 @@ export interface VideoPlayerStyle extends ComponentStyle {
export const component = {
id: 'VideoPlayer',
name: '视频播放',
icon: 'ep:video-play',
icon: 'lucide:video',
property: {
videoUrl: '',
posterUrl: '',

View File

@@ -27,14 +27,11 @@ export interface DiyComponentLibrary {
components: string[]; // 组件列表
}
// 组件样式
/** 组件样式 */
export interface ComponentStyle {
// 背景类型
bgType: 'color' | 'img';
// 背景颜色
bgColor: string;
// 背景图片
bgImg: string;
bgType: 'color' | 'img'; // 背景类型
bgColor: string; // 背景颜色
bgImg: string; // 背景图片
// 外边距
margin: number;
marginTop: number;

View File

@@ -63,9 +63,8 @@ const handleDelete = function (index: number) {
>
<ElTooltip content="拖动排序">
<IconifyIcon
icon="ic:round-drag-indicator"
class="drag-icon cursor-move"
style="color: #8a909c"
icon="lucide:move"
class="drag-icon cursor-move text-gray-500"
/>
</ElTooltip>
<ElTooltip content="删除">
@@ -90,7 +89,7 @@ const handleDelete = function (index: number) {
:disabled="limit > 0 && formData.length >= limit"
@click="handleAdd"
>
<IconifyIcon icon="ep:plus" /><span>添加</span>
<IconifyIcon icon="lucide:plus" /><span>添加</span>
</ElButton>
</ElTooltip>
</template>

View File

@@ -183,7 +183,7 @@ function exitHotAreaSelectMode() {
* 迭代魔方矩阵
* @param callback 回调
*/
const eachCube = (callback: (x: number, y: number, cube: Cube) => void) => {
function eachCube(callback: (x: number, y: number, cube: Cube) => void) {
for (const [x, row] of cubes.value.entries()) {
if (!row) continue;
for (const [y, cube] of row.entries()) {
@@ -192,7 +192,7 @@ const eachCube = (callback: (x: number, y: number, cube: Cube) => void) => {
}
}
}
};
}
</script>
<template>
<div class="relative">

View File

@@ -133,7 +133,7 @@ function emitActivityChange() {
class="hover:border-primary hover:bg-primary/5 flex h-[60px] w-[60px] cursor-pointer items-center justify-center rounded-lg border-2 border-dashed transition-colors"
@click="handleOpenActivitySelect"
>
<IconifyIcon icon="ep:plus" class="text-xl text-gray-400" />
<IconifyIcon icon="lucide:plus" class="text-xl text-gray-400" />
</div>
</ElTooltip>
</div>

View File

@@ -81,11 +81,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
<template>
<Page auto-content-height>
<DocAlert
title="会员等级、积分、签到"
url="https://doc.iocoder.cn/member/level/"
/>
<template #doc>
<DocAlert
title="会员等级、积分、签到"
url="https://doc.iocoder.cn/member/level/"
/>
</template>
<FormModal @success="handleRefresh" />
<Grid table-title="等级列表">
<template #toolbar-tools>

View File

@@ -42,10 +42,12 @@ const [Grid] = useVbenVxeGrid({
<template>
<Page auto-content-height>
<DocAlert
title="会员等级、积分、签到"
url="https://doc.iocoder.cn/member/level/"
/>
<template #doc>
<DocAlert
title="会员等级、积分、签到"
url="https://doc.iocoder.cn/member/level/"
/>
</template>
<Grid table-title="积分记录列表" />
</Page>

View File

@@ -2,7 +2,6 @@
- Copyright (C) 2018-2019
- All rights reserved, Designed By www.joolun.com
芋道源码
移除 avue 组件使用 ElementUI 原生组件
-->
<script lang="ts" setup>
import { onMounted, reactive, ref } from 'vue';

View File

@@ -1,4 +1,4 @@
.avue-card {
.mp-card {
&__item {
box-sizing: border-box;
height: 200px;
@@ -99,18 +99,18 @@
}
/** joolun 额外加的 */
.avue-comment__main {
.mp-comment__main {
flex: unset !important;
margin: 0 8px !important;
border-radius: 5px !important;
}
.avue-comment__header {
.mp-comment__header {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.avue-comment__body {
.mp-comment__body {
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
}

View File

@@ -1,5 +1,5 @@
/* 来自 https://github.com/nmxiaowei/avue/blob/master/styles/src/element-ui/comment.scss */
.avue-comment {
.mp-comment {
display: flex;
align-items: flex-start;
margin-bottom: 30px;
@@ -7,7 +7,7 @@
&--reverse {
flex-direction: row-reverse;
.avue-comment__main {
.mp-comment__main {
&::before,
&::after {
right: -8px;

View File

@@ -55,7 +55,7 @@ const getNickname = (sendFrom: SendFromType) =>
<div
class="flex items-center justify-between rounded-t-[5px] border-b border-[#eee] bg-[#f8f8f8] px-[15px] py-[5px]"
>
<div class="avue-comment__create_time">
<div class="mp-comment__create_time">
{{ formatDateTime(item.createTime) }}
</div>
</div>

View File

@@ -44,18 +44,15 @@ defineExpose({
target="_blank"
:href="hqMusicUrl ? hqMusicUrl : musicUrl"
>
<div
class="avue-card__body"
style="padding: 10px; background-color: #fff; border-radius: 5px"
>
<div class="avue-card__avatar">
<div class="mp-card__body bg-background rounded-sm p-2.5">
<div class="mp-card__avatar">
<img :src="thumbMediaUrl" alt="" />
</div>
<div class="avue-card__detail">
<div class="avue-card__title" style="margin-bottom: unset">
<div class="mp-card__detail">
<div class="mp-card__title" style="margin-bottom: unset">
{{ title }}
</div>
<div class="avue-card__info" style="height: unset">
<div class="mp-card__info" style="height: unset">
{{ description }}
</div>
</div>

View File

@@ -6,9 +6,7 @@
代码优化补充注释提升阅读性
-->
<script lang="ts" setup>
import { ElImage } from 'element-plus';
defineOptions({ name: 'News' });
defineOptions({ name: 'WxNews' });
const props = withDefaults(
defineProps<{
@@ -31,7 +29,7 @@ defineExpose({
<a v-if="index === 0" :href="article.url" target="_blank">
<div class="news-main">
<div class="news-content">
<ElImage
<el-image
:src="article.picUrl || article.thumbUrl"
class="material-img"
style="width: 100%; height: 120px"
@@ -48,7 +46,7 @@ defineExpose({
<div class="news-content-item">
<div class="news-content-item-title">{{ article.title }}</div>
<div class="news-content-item-img">
<ElImage
<img
:src="article.picUrl || article.thumbUrl"
class="material-img"
height="100%"

View File

@@ -239,7 +239,9 @@ onMounted(async () => {
<template>
<Page auto-content-height>
<DocAlert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
<template #doc>
<DocAlert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
</template>
<FormModal
@success="

View File

@@ -0,0 +1,116 @@
.mp-card {
&__item {
box-sizing: border-box;
height: 200px;
margin-bottom: 16px;
font-size: 14px;
font-feature-settings: 'tnum';
font-variant: tabular-nums;
line-height: 1.5;
color: rgb(0 0 0 / 65%);
cursor: pointer;
list-style: none;
background-color: #fff;
border: 1px solid #e8e8e8;
&:hover {
border-color: rgb(0 0 0 / 9%);
box-shadow: 0 2px 8px rgb(0 0 0 / 9%);
}
&--add {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
font-size: 16px;
color: rgb(0 0 0 / 45%);
background-color: #fff;
border: 1px dashed #000;
border-color: #d9d9d9;
border-radius: 2px;
i {
margin-right: 10px;
}
&:hover {
color: #40a9ff;
background-color: #fff;
border-color: #40a9ff;
}
}
}
&__body {
display: flex;
padding: 24px;
}
&__detail {
flex: 1;
}
&__avatar {
width: 48px;
height: 48px;
margin-right: 12px;
overflow: hidden;
border-radius: 48px;
img {
width: 100%;
height: 100%;
}
}
&__title {
margin-bottom: 12px;
font-size: 16px;
color: rgb(0 0 0 / 85%);
&:hover {
color: #1890ff;
}
}
&__info {
display: -webkit-box;
height: 64px;
overflow: hidden;
-webkit-line-clamp: 3;
color: rgb(0 0 0 / 45%);
-webkit-box-orient: vertical;
}
&__menu {
display: flex;
justify-content: space-around;
height: 50px;
line-height: 50px;
color: rgb(0 0 0 / 45%);
text-align: center;
background: #f7f9fa;
&:hover {
color: #1890ff;
}
}
}
/** joolun 额外加的 */
.mp-comment__main {
flex: unset !important;
margin: 0 8px !important;
border-radius: 5px !important;
}
.mp-comment__header {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.mp-comment__body {
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
}

View File

@@ -0,0 +1,109 @@
/* 来自 https://github.com/nmxiaowei/avue/blob/master/styles/src/element-ui/comment.scss */
.mp-comment {
display: flex;
align-items: flex-start;
margin-bottom: 30px;
&--reverse {
flex-direction: row-reverse;
.mp-comment__main {
&::before,
&::after {
right: -8px;
left: auto;
border-width: 8px 0 8px 8px;
}
&::before {
border-left-color: #dedede;
}
&::after {
margin-right: 1px;
margin-left: auto;
border-left-color: #f8f8f8;
}
}
}
&__avatar {
box-sizing: border-box;
width: 48px;
height: 48px;
vertical-align: middle;
border: 1px solid transparent;
border-radius: 50%;
}
&__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 5px 15px;
background: #f8f8f8;
border-bottom: 1px solid #eee;
}
&__author {
font-size: 14px;
font-weight: 700;
color: #999;
}
&__main {
position: relative;
flex: 1;
margin: 0 20px;
border: 1px solid #dedede;
border-radius: 2px;
&::before,
&::after {
position: absolute;
top: 10px;
right: 100%;
left: -8px;
display: block;
width: 0;
height: 0;
pointer-events: none;
content: ' ';
border-color: transparent;
border-style: solid solid outset;
border-width: 8px 8px 8px 0;
}
&::before {
z-index: 1;
border-right-color: #dedede;
}
&::after {
z-index: 2;
margin-left: 1px;
border-right-color: #f8f8f8;
}
}
&__body {
padding: 15px;
overflow: hidden;
font-family:
'Segoe UI', 'Lucida Grande', Helvetica, Arial, 'Microsoft YaHei',
FreeSans, Arimo, 'Droid Sans', 'wenquanyi micro hei', 'Hiragino Sans GB',
'Hiragino Sans GB W3', FontAwesome, sans-serif;
font-size: 14px;
color: #333;
background: #fff;
}
blockquote {
padding: 1px 0 1px 15px;
margin: 0;
font-family:
Georgia, 'Times New Roman', Times, Kai, 'Kaiti SC', KaiTi, BiauKai,
FontAwesome, serif;
border-left: 4px solid #ddd;
}
}