mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-06 17:09:08 +08:00
refactor(im): 重构聊天面板UI组件并修复多处交互问题
- 将头像组件从 dicebear 外链改为本地首字+底色生成,新增 avatarColor 工具函数 - 重构订单卡片(footPrint.vue)布局,优化商品与订单信息展示结构 - 重构店铺详情卡片(storeDetail.vue),统一信息行与评分行样式 - 移除消息气泡中的昵称时间行,调整气泡背景色与头像内边距 - 修复 face 变量名与全局组件 <face> 冲突导致头像URL被当作标签渲染 - 将 unload 事件监听替换为 pagehide,兼容 Chrome 弃用策略 - 修复搜索关键词未转义正则元字符导致输入特殊字符时抛异常 - 修复 WebSocket onMessage 对无 text 字段的推送消息误调用 JSON.parse - 将 setup 中同步调用全局 mixin 方法移至 onMounted,避免代理未就绪 - 商品/订单图片统一添加 onerror 默认图兜底 - 聊天列表消息预览支持 emoji 图片渲染(textReplaceEmoji) - 搜索框增加 clearable 属性及"没有找到相关好友"空态提示 - 锁定 element-plus 版本为 2.14.1 防止自动升级引入兼容问题 - 日期格式化分隔符由斜杠改为横杠 - 提现设置页新增提现规则说明字段,满足微信审核公示要求
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
"axios": "^1.7.9",
|
||||
"babel-plugin-prismjs": "^2.0.1",
|
||||
"core-js": "^3.49.0",
|
||||
"element-plus": "^2.14.1",
|
||||
"element-plus": "2.14.1",
|
||||
"js-audio-recorder": "^1.0.6",
|
||||
"js-base64": "^2.5.1",
|
||||
"nprogress": "^0.2.0",
|
||||
|
||||
@@ -34,11 +34,12 @@ function unloadHandler (e) {
|
||||
|
||||
onMounted(() => {
|
||||
setRefreshView(refreshView);
|
||||
window.addEventListener('beforeunload', e => beforeunloadHandler(e))
|
||||
window.addEventListener('unload', e => unloadHandler(e))
|
||||
window.addEventListener('beforeunload', beforeunloadHandler)
|
||||
// unload 事件已被 Chrome 弃用(Permissions-Policy 禁止),改用 pagehide
|
||||
window.addEventListener('pagehide', unloadHandler)
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('beforeunload', e => beforeunloadHandler(e))
|
||||
window.removeEventListener('unload', e => unloadHandler(e))
|
||||
window.removeEventListener('beforeunload', beforeunloadHandler)
|
||||
window.removeEventListener('pagehide', unloadHandler)
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick" type="card" :stretch=true>
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick" :stretch=true>
|
||||
<el-tab-pane :label="toUser.storeFlag ? '正在咨询' : '他的足迹'" name="history">
|
||||
<div>
|
||||
<GoodsLink :goodsDetail="goodsDetail" v-if="toUser.userId === goodsDetail.storeId"
|
||||
@@ -306,4 +306,20 @@ function formatDateToString(date) {
|
||||
:deep(.el-tabs__header) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 高度链:tabs 撑满面板,pane 高度传递给内部滚动容器 */
|
||||
:deep(.el-tabs) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__content) {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
:deep(.el-tab-pane) {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</aside>
|
||||
<aside class="avatar-column">
|
||||
<div class="avatar-box" v-if="item.float == 'right'">
|
||||
<face :text="face" v-if="face"></face>
|
||||
<face :text="userFace" v-if="userFace"></face>
|
||||
<face-null :text="name" v-else></face-null>
|
||||
</div>
|
||||
<div class="avatar-box" v-if="item.float == 'left'">
|
||||
@@ -53,14 +53,10 @@
|
||||
</aside>
|
||||
<main class="main-column">
|
||||
<div class="talk-content">
|
||||
<span class="nickname">
|
||||
{{ item.float == "right" ? name : toUser.name }} |
|
||||
{{ unixToDate(item.createTime, "MM月dd日 hh:mm") }}
|
||||
</span>
|
||||
<div class="flex flex-a-c">
|
||||
<legacy-el-icon name="el-icon-refresh-left" class="again main-color" @click="againSendMessage(item)" v-if="item.webSocketStatus" />
|
||||
<!-- 文本消息 -->
|
||||
<div v-if="item.messageType == 'MESSAGE'" style="background-color: #d0e9ff;color: black;"
|
||||
<div v-if="item.messageType == 'MESSAGE'"
|
||||
class="text-message" :class="{
|
||||
left: item.float == 'left',
|
||||
right: item.float == 'right',
|
||||
@@ -79,7 +75,7 @@
|
||||
}">
|
||||
<div class="base" @click="linkToGoods(item.text.goodsId, item.text.id)">
|
||||
<div>
|
||||
<img :src="item.text.thumbnail" class="image" />
|
||||
<img :src="item.text.thumbnail" :onerror="defaultAvatar" class="image" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="goods_name">
|
||||
@@ -106,7 +102,7 @@
|
||||
<div class="goods-shared-box">
|
||||
|
||||
<div class="goods-item" v-for="(order,orderIndex) in item.text.orderItems" :key="orderIndex">
|
||||
<img :src="order.image" style="height: 100px;width: 100px;" />
|
||||
<img :src="order.image" :onerror="defaultAvatar" style="height: 100px;width: 100px;" />
|
||||
<div>
|
||||
<span class="orderGoodsName">{{ order.name }}</span>
|
||||
<div class="goods-item-price">
|
||||
@@ -212,6 +208,7 @@ import SocketInstance from "@/im-server/socket-instance";
|
||||
import { SvgMentionDown } from "@/core/icons";
|
||||
import { formatTime, parseTime, copyTextToClipboard } from "@/utils/functions";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { useTalkStore } from "@/stores/talk";
|
||||
import { useNotifyStore } from "@/stores/notify";
|
||||
import { useSettingsStore } from "@/stores/settings";
|
||||
@@ -259,6 +256,7 @@ const emit = defineEmits(["close-talk"]);
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
const talkStore = useTalkStore();
|
||||
const notifyStore = useNotifyStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
@@ -306,13 +304,16 @@ const panelHeader = ref(null);
|
||||
|
||||
const talkItems = computed(() => talkStore.talkItems);
|
||||
const name = computed(() => userStore.name);
|
||||
const face = computed(() => userStore.face);
|
||||
// 不能命名为 face:会遮蔽 <face> 组件解析,头像 URL 会被当作标签名渲染
|
||||
const userFace = computed(() => userStore.face);
|
||||
const unreadMessage = computed(() => talkStore.unreadMessage);
|
||||
const inputEvent = computed(() => notifyStore.inputEvent);
|
||||
const id = computed(() => userStore.id);
|
||||
const records = computed(() => dialogueStore.records);
|
||||
const index_name = computed(() => dialogueStore.index_name);
|
||||
const toUser = computed(() => userStore.toUser);
|
||||
// 商品/订单图片加载失败时的默认图(onerror 属性值)
|
||||
const defaultAvatar = computed(() => appStore.defaultAvatar);
|
||||
|
||||
const sendTime = formatTime;
|
||||
|
||||
@@ -1147,7 +1148,7 @@ function talkPanelScrollBottom() {
|
||||
flex-shrink: 0;
|
||||
order: 1;
|
||||
user-select: none;
|
||||
padding-top: 25px;
|
||||
padding-top: 8px;
|
||||
|
||||
i {
|
||||
color: #ccc;
|
||||
@@ -1168,7 +1169,7 @@ function talkPanelScrollBottom() {
|
||||
align-items: center;
|
||||
order: 2;
|
||||
user-select: none;
|
||||
padding-top: 22px;
|
||||
padding-top: 5px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -1206,13 +1207,6 @@ function talkPanelScrollBottom() {
|
||||
align-items: flex-start;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
|
||||
.nickname {
|
||||
font-size: 12px;
|
||||
color: #a7a0a0;
|
||||
margin: -4px 0 4px -8px;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -1250,8 +1244,8 @@ function talkPanelScrollBottom() {
|
||||
}
|
||||
}
|
||||
|
||||
@bg-left-color: #f5f5f5;
|
||||
@bg-right-color: #ffffff;
|
||||
@bg-left-color: #f2f2f2;
|
||||
@bg-right-color: #409eff;
|
||||
|
||||
.text-message {
|
||||
position: relative;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<div class="box" :style="{height:toUser.storeFlag ? '858px' : '815px'}" @scroll="scrollBottom">
|
||||
<div class="box" @scroll="scrollBottom">
|
||||
<div class="tab">
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick" :stretch=true>
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick" type="card" :stretch=true>
|
||||
<el-tab-pane label="最近浏览" name="goods">
|
||||
<dl v-if='list.length' class='base-list'>
|
||||
<dd :key="index" @click="linkToGoods(item.goodsId, item.id)" v-for="(item,index) in list">
|
||||
<div class="base">
|
||||
<div>
|
||||
<img style="width: 60px; height: 60px;margin-left:20px;box-sizing: border-box;" :src="item.thumbnail"
|
||||
class="image" />
|
||||
:onerror="defaultAvatar" class="image" />
|
||||
</div>
|
||||
<div class="recent_views">
|
||||
<a class="goods_name">{{ item.goodsName }}</a>
|
||||
@@ -35,24 +35,8 @@
|
||||
<dl class='base-order-list' v-if='orderList.length'>
|
||||
<dd v-for="(item, index) in orderList" :key="index">
|
||||
<div class="order-list">
|
||||
<div class="order-top order-padding">
|
||||
<span class="order-sn" @click="linkToOrders(item.sn)">订单号:{{ item.sn }}</span>
|
||||
</div>
|
||||
<div class="order-section order-padding">
|
||||
<div class="order-items" v-for="(order,orderIndex) in item.orderItems" :key="orderIndex">
|
||||
<img :src="order.image" alt="">
|
||||
<span class="order-goods-name" @click="linkToOrders(item.sn)"> {{ order.name }}</span>
|
||||
<span class="price">{{ $filters.unitPrice(order.goodsPrice, "¥") }}</span>
|
||||
</div>
|
||||
<!-- <img :src="item.groupImages" alt=""> -->
|
||||
|
||||
<div class="order-btn ">
|
||||
<el-button class="store-button" v-if="item.btnHide == 1 && toUser.storeFlag"
|
||||
size="small" @click="submitSendOrderMessage(item, index)" plain>发送</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="order-footer order-padding">
|
||||
<span></span>
|
||||
<div class="order-head">
|
||||
<span class="order-sn" :title="item.sn" @click="linkToOrders(item.sn)">订单号:{{ item.sn }}</span>
|
||||
<el-tag size="small" :type="col[item.orderStatus]">{{
|
||||
item.orderStatus == 'STAY_PICKED_UP' ? '待自提'
|
||||
: item.orderStatus == 'CANCELLED' ? '已取消' : item.orderStatus == 'UNPAID' ? '未付款' : item.orderStatus
|
||||
@@ -62,9 +46,18 @@
|
||||
''
|
||||
}}</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="order-items" v-for="(order,orderIndex) in item.orderItems" :key="orderIndex">
|
||||
<img :src="order.image" :onerror="defaultAvatar" alt="">
|
||||
<div class="order-item-info">
|
||||
<span class="order-goods-name" :title="order.name" @click="linkToOrders(item.sn)">{{ order.name }}</span>
|
||||
<span class="price">{{ $filters.unitPrice(order.goodsPrice, "¥") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="order-foot" v-if="item.btnHide == 1 && toUser.storeFlag">
|
||||
<el-button class="store-button" size="small" @click="submitSendOrderMessage(item, index)" plain>发送</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<div v-else class='no-more'>
|
||||
@@ -80,9 +73,11 @@
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useTalkStore } from "@/stores/talk";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
|
||||
const talkStore = useTalkStore();
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const emit = defineEmits(["loadMore", "sendMessage"]);
|
||||
|
||||
@@ -118,6 +113,8 @@ const col = ref({
|
||||
const talkItems = computed(() => talkStore.talkItems);
|
||||
const id = computed(() => userStore.id);
|
||||
const toUser = computed(() => userStore.toUser);
|
||||
// 图片加载失败时的默认图(onerror 属性值)
|
||||
const defaultAvatar = computed(() => appStore.defaultAvatar);
|
||||
|
||||
//跳转订单列表
|
||||
|
||||
@@ -204,7 +201,9 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.box {
|
||||
max-width: 362px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.top {
|
||||
text-align: center;
|
||||
@@ -237,57 +236,51 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.order-list {
|
||||
padding: 9px;
|
||||
transition: 0.35s;
|
||||
cursor: pointer;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
background: #fff;
|
||||
color: #5a606b;
|
||||
box-sizing: border-box;
|
||||
transition: border-color .2s;
|
||||
|
||||
&:hover {
|
||||
border-color: #b3d3f9;
|
||||
}
|
||||
}
|
||||
|
||||
.order-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding-bottom: 8px;
|
||||
margin-bottom: 8px;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.order-sn {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.box::-webkit-scrollbar {
|
||||
width: 0px !important;
|
||||
}
|
||||
.order-section {
|
||||
margin: 10px 0;
|
||||
font-size: 12px;
|
||||
img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.order-footer {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.order-goods-name {
|
||||
width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: absolute;
|
||||
margin-left: 10px;
|
||||
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.orderSn {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.order-btn {
|
||||
.order-foot {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
position: relative;
|
||||
bottom: 55px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.goods_name {
|
||||
@@ -300,7 +293,7 @@ onMounted(() => {
|
||||
|
||||
:deep(.el-tabs__header ) {
|
||||
position: absolute;
|
||||
width: 362px;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
@@ -329,10 +322,6 @@ onMounted(() => {
|
||||
// width: 350px;
|
||||
}
|
||||
|
||||
.order-padding {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.base {
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
@@ -354,12 +343,12 @@ onMounted(() => {
|
||||
|
||||
}
|
||||
}
|
||||
.base:hover,.order-list:hover{
|
||||
.base:hover{
|
||||
background: #ededed;
|
||||
padding-left: 14px;
|
||||
>.review{
|
||||
opacity: 1;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
.goods-price{
|
||||
@@ -371,9 +360,6 @@ onMounted(() => {
|
||||
.separate {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.order-sn{
|
||||
font-size: 12px;
|
||||
}
|
||||
// .el-tabs--card {
|
||||
// height: calc(100vh - 110px);
|
||||
// overflow-y: auto;
|
||||
@@ -383,17 +369,54 @@ onMounted(() => {
|
||||
// overflow-y: auto;
|
||||
// }
|
||||
.base-list{
|
||||
padding-bottom: 120px;
|
||||
padding: 10px 10px 12px;
|
||||
}
|
||||
.base-order-list{
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
.price{
|
||||
color: red;
|
||||
margin-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px 10px 12px;
|
||||
}
|
||||
.order-items{
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
object-fit: cover;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.order-item-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.order-goods-name {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.price {
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
color: #f23030;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
当前浏览
|
||||
<div class="base">
|
||||
<div>
|
||||
<img :src="goodsDetail.thumbnail" class="image" />
|
||||
<img :src="goodsDetail.thumbnail" :onerror="defaultAvatar" class="image" />
|
||||
</div>
|
||||
<div style="margin-left: 13px">
|
||||
<a @click="linkToGoods(goodsDetail.goodsId, goodsDetail.id)"> {{ goodsDetail.goodsName }} </a>
|
||||
@@ -23,10 +23,12 @@
|
||||
import { computed, onMounted, nextTick, ref } from "vue";
|
||||
import SocketInstance from "@/im-server/socket-instance";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { useDialogueStore } from "@/stores/dialogue";
|
||||
import { useTalkStore } from "@/stores/talk";
|
||||
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
const dialogueStore = useDialogueStore();
|
||||
const talkStore = useTalkStore();
|
||||
|
||||
@@ -44,6 +46,8 @@ const talkItems = computed(() => talkStore.talkItems);
|
||||
const id = computed(() => userStore.id);
|
||||
const index_name = computed(() => dialogueStore.index_name);
|
||||
const toUser = computed(() => userStore.toUser);
|
||||
// 图片加载失败时的默认图(onerror 属性值)
|
||||
const defaultAvatar = computed(() => appStore.defaultAvatar);
|
||||
|
||||
onMounted(() => {
|
||||
console.log(props.goodsDetail)
|
||||
|
||||
@@ -1,100 +1,157 @@
|
||||
<template>
|
||||
<div style="width: 362px;">
|
||||
<div class="store-card">
|
||||
<div class="base">
|
||||
<div>
|
||||
<img :src="storeInfo.storeLogo" class="image" />
|
||||
</div>
|
||||
<img :src="storeInfo.storeLogo" :onerror="onError" class="image" alt="" />
|
||||
<div class="store-detail">
|
||||
<div>
|
||||
<div class="div-zoom">
|
||||
<span class="title_left_name"> 店铺名称:</span> <span class="title_right_name">{{ storeInfo.storeName }}</span>
|
||||
<el-tag style="margin-left: 10px;" type="danger" v-if="storeInfo.selfOperated" size="small">自营</el-tag>
|
||||
<div class="store-name">
|
||||
<span class="name-text" :title="storeInfo.storeName">{{ storeInfo.storeName }}</span>
|
||||
<el-tag type="danger" v-if="storeInfo.selfOperated" size="small">自营</el-tag>
|
||||
</div>
|
||||
<div>
|
||||
<span class="title_left_name"> 联系方式:</span> <span class="title_right_name">{{ storeInfo.memberName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-button class="store-button" @click="linkToStore(storeInfo.id)" size="small"
|
||||
plain>进入店铺</el-button>
|
||||
<div class="store-actions">
|
||||
<el-button @click="linkToStore(storeInfo.id)" size="small" plain>进入店铺</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-list">
|
||||
<div class="info-row" v-for="row in infoRows" :key="row.label">
|
||||
<span class="label">{{ row.label }}</span>
|
||||
<span class="value" :title="row.value">{{ row.value || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<div class="separate">店铺评分: <el-rate v-model="storeInfo.serviceScore" disabled show-score text-color="#ff9900"
|
||||
score-template="{value}">
|
||||
</el-rate></div>
|
||||
<div class="separate">服务评分: <el-rate v-model="storeInfo.descriptionScore" disabled show-score text-color="#ff9900"
|
||||
score-template="{value}">
|
||||
</el-rate></div>
|
||||
<div class="separate">物流评分: <el-rate v-model="storeInfo.deliveryScore" disabled show-score text-color="#ff9900"
|
||||
score-template="{value}">
|
||||
</el-rate></div>
|
||||
<div class="rate-row" v-for="row in rates" :key="row.label">
|
||||
<span class="rate-label">{{ row.label }}</span>
|
||||
<el-rate :model-value="row.score" disabled show-score text-color="#ff9900" score-template="{value}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { computed } from "vue";
|
||||
import { defaultAvatar } from "@/assets/images";
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
storeInfo: {
|
||||
type: Object,
|
||||
default: null,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const value = ref(null);
|
||||
const colors = ref(['#99A9BF', '#F7BA2A', '#FF9900']);
|
||||
const onError = "this.src='" + defaultAvatar + "'";
|
||||
|
||||
const infoRows = computed(() => [
|
||||
{ label: "店铺地址", value: props.storeInfo.storeAddressPath },
|
||||
{ label: "详细地址", value: props.storeInfo.storeAddressDetail },
|
||||
{ label: "店铺简介", value: props.storeInfo.storeDesc },
|
||||
]);
|
||||
|
||||
// 与买家端字段口径一致:店铺评分=描述相符 descriptionScore,服务评分=serviceScore
|
||||
const rates = computed(() => [
|
||||
{ label: "店铺评分", score: Number(props.storeInfo.descriptionScore) || 0 },
|
||||
{ label: "服务评分", score: Number(props.storeInfo.serviceScore) || 0 },
|
||||
{ label: "物流评分", score: Number(props.storeInfo.deliveryScore) || 0 },
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.wrapper{
|
||||
.store-card {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.base {
|
||||
padding: 16px;
|
||||
border-top: 1px solid #f5eeee;
|
||||
}
|
||||
.title_left_name {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
display: flex;
|
||||
|
||||
.image {
|
||||
height: 72px;
|
||||
width: 72px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 6px;
|
||||
object-fit: cover;
|
||||
background: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
.title_right_name {
|
||||
font-size: 12px;
|
||||
color: #343036;
|
||||
|
||||
}
|
||||
|
||||
.store-button {
|
||||
background-color: white;
|
||||
|
||||
}
|
||||
.store-detail{
|
||||
.store-detail {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-left: 13px;
|
||||
display:flex;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.base {
|
||||
padding: 16px;
|
||||
margin-top: 5px;
|
||||
|
||||
.store-name {
|
||||
display: flex;
|
||||
.image {
|
||||
height: 100px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
width: 100px
|
||||
.name-text {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #343036;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.store-actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.info-list {
|
||||
padding: 0 16px 12px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-top: 8px;
|
||||
|
||||
.label {
|
||||
width: 60px;
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 12px;
|
||||
color: #343036;
|
||||
line-height: 20px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.separate {
|
||||
margin-top: 8px;
|
||||
// display: inline;
|
||||
.wrapper {
|
||||
padding: 12px 16px 16px;
|
||||
border-top: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.rate-row {
|
||||
display: flex;
|
||||
font-size: 12px;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
|
||||
color: #a1a6af;
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.rate-label {
|
||||
width: 60px;
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
color: #a1a6af;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
<template>
|
||||
<img :src="'https://avatars.dicebear.com/api/initials/'+text+'.svg?fontSize=38'" alt=""/>
|
||||
<i class="face-initial" :style="{ backgroundColor: color }">{{ initial }}</i>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
text:{
|
||||
type:null,
|
||||
default:''
|
||||
}
|
||||
})
|
||||
import { computed } from "vue";
|
||||
import { avatarColor } from "@/utils/functions";
|
||||
|
||||
const props = defineProps({
|
||||
text: {
|
||||
type: null,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
// 无头像时用「底色 + 首字」本地生成,替代原来的 dicebear 外链
|
||||
const initial = computed(() => String(props.text || "").slice(0, 1) || "?");
|
||||
const color = computed(() => avatarColor(String(props.text || "")));
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.face-initial {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
font-style: normal;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,20 +1,53 @@
|
||||
<template>
|
||||
<img :src="text || 'https://avatars.dicebear.com/api/initials/'+name+'.svg?fontSize=38'" alt=""/>
|
||||
<span class="face-wrap">
|
||||
<img v-if="text" :src="text" :onerror="onError" alt="" />
|
||||
<i v-else class="face-initial" :style="{ backgroundColor: color }">{{ initial }}</i>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
text: {
|
||||
type: null,
|
||||
default: ''
|
||||
},
|
||||
name: {
|
||||
type: null,
|
||||
default: ''
|
||||
},
|
||||
});
|
||||
import { computed } from "vue";
|
||||
import { avatarColor } from "@/utils/functions";
|
||||
import { defaultAvatar } from "@/assets/images";
|
||||
|
||||
const props = defineProps({
|
||||
text: {
|
||||
type: null,
|
||||
default: ''
|
||||
},
|
||||
name: {
|
||||
type: null,
|
||||
default: ''
|
||||
},
|
||||
});
|
||||
|
||||
// 无头像时用「底色 + 首字」本地生成,替代原来的 dicebear 外链
|
||||
const initial = computed(() => String(props.name || "").slice(0, 1) || "?");
|
||||
const color = computed(() => avatarColor(String(props.name || "")));
|
||||
const onError = "this.src='" + defaultAvatar + "'";
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
.face-wrap {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
</style>
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.face-initial {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
font-style: normal;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -273,22 +273,17 @@ class WsSocket {
|
||||
*/
|
||||
onMessage(evt) {
|
||||
let result = this.onParse(evt);
|
||||
if (
|
||||
this.onParse(evt).text.includes("goodsName") ||
|
||||
this.onParse(evt).text.includes("groupName")
|
||||
) {
|
||||
let params = {
|
||||
...this.onParse(evt),
|
||||
text: JSON.parse(this.onParse(evt).text),
|
||||
};
|
||||
this.onCallBacks["event_talk"](params);
|
||||
} else {
|
||||
let params = {
|
||||
...this.onParse(evt),
|
||||
text: this.onParse(evt).text,
|
||||
};
|
||||
this.onCallBacks["event_talk"](params);
|
||||
}
|
||||
// 部分推送(在线状态、系统通知等)没有 text 字段,不做商品/群名解析直接透传
|
||||
const text = result.text;
|
||||
let params = {
|
||||
...result,
|
||||
text:
|
||||
typeof text === "string" &&
|
||||
(text.includes("goodsName") || text.includes("groupName"))
|
||||
? JSON.parse(text)
|
||||
: text,
|
||||
};
|
||||
this.onCallBacks["event_talk"](params);
|
||||
// 指定推送消息
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -390,6 +390,19 @@ export function hidePhone (phone) {
|
||||
return phone.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名字生成稳定的头像底色(本地首字头像用,替代 dicebear 外链)
|
||||
*
|
||||
* @param {String} name
|
||||
*/
|
||||
export function avatarColor (name = "") {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < name.length; i++) {
|
||||
hash = (hash * 31 + name.charCodeAt(i)) >>> 0;
|
||||
}
|
||||
return `hsl(${hash % 360}, 55%, 55%)`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 人性化显示时间
|
||||
*
|
||||
@@ -409,15 +422,15 @@ export function beautifyTime (datetime = "") {
|
||||
}
|
||||
|
||||
if (time.getTime() < outTime.getTime()) {
|
||||
return parseTime(outTime, "{y}/{m}/{d}");
|
||||
return parseTime(outTime, "{y}-{m}-{d}");
|
||||
}
|
||||
|
||||
if (time.getFullYear() != outTime.getFullYear()) {
|
||||
return parseTime(outTime, "{y}/{m}/{d}");
|
||||
return parseTime(outTime, "{y}-{m}-{d}");
|
||||
}
|
||||
|
||||
if (time.getMonth() != outTime.getMonth()) {
|
||||
return parseTime(outTime, "{m}/{d}");
|
||||
return parseTime(outTime, "{m}-{d}");
|
||||
}
|
||||
|
||||
if (time.getDate() != outTime.getDate()) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<el-header height="118px" class="logo-header">
|
||||
<div class="user-login" v-popover:usercard>
|
||||
<div class="user-box">
|
||||
<face :text="face"></face>
|
||||
<face :text="userFace"></face>
|
||||
</div>
|
||||
</div>
|
||||
<p class="user-status">
|
||||
@@ -66,7 +66,8 @@ const userStore = useUserStore();
|
||||
const notifyStore = useNotifyStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
const face = computed(() => userStore.face);
|
||||
// 不能命名为 face:会遮蔽 <face> 组件解析,头像 URL 会被当作标签名渲染
|
||||
const userFace = computed(() => userStore.face);
|
||||
const name = computed(() => userStore.name);
|
||||
const id = computed(() => userStore.id);
|
||||
const defaultAvatar = computed(() => appStore.defaultAvatar);
|
||||
@@ -107,8 +108,8 @@ function setApplyNum () {
|
||||
<style lang="less" >
|
||||
.main-layout {
|
||||
position: fixed;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
width: 88%;
|
||||
height: 92%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
@@ -119,8 +120,8 @@ function setApplyNum () {
|
||||
border-radius: 5px;
|
||||
|
||||
&.full-mode {
|
||||
width: 1200px;
|
||||
height: 85%;
|
||||
width: 92%;
|
||||
height: 96%;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<el-header height="60px" class="header">
|
||||
<div class="user-login">
|
||||
<div class="user-box">
|
||||
<face :text="face" :name="name" class="user-face"></face>
|
||||
<face :text="userFace" :name="name" class="user-face"></face>
|
||||
</div>
|
||||
</div>
|
||||
<p class="user-status">
|
||||
@@ -20,7 +20,7 @@
|
||||
</el-header>
|
||||
<el-header height="60px" class="header">
|
||||
<div class="from-search">
|
||||
<el-input v-model="input" :prefix-icon="$legacyIcon('el-icon-search')" placeholder="搜索好友" size="small" />
|
||||
<el-input v-model="input" :prefix-icon="$legacyIcon('el-icon-search')" placeholder="搜索好友" size="small" clearable />
|
||||
</div>
|
||||
</el-header>
|
||||
<!-- <el-header height="118px" class="logo-header">
|
||||
@@ -58,6 +58,10 @@
|
||||
暂无聊天消息
|
||||
</p>
|
||||
|
||||
<p v-show="loadStatus === 1 && talkNum > 0 && userTalkItem.length === 0" class="empty-data">
|
||||
没有找到相关好友
|
||||
</p>
|
||||
|
||||
<p v-show="loadStatus === 1 && talkNum > 0" class="main-menu">
|
||||
<span class="title">对话记录({{ talkNum }})</span>
|
||||
|
||||
@@ -108,7 +112,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="talk-message">
|
||||
<span v-if="item.lastMessageType === 'MESSAGE'">{{ item.lastTalkMessage }}</span>
|
||||
<span v-if="item.lastMessageType === 'MESSAGE'" v-html="textReplaceEmoji(item.lastTalkMessage)"></span>
|
||||
<span v-if="item.lastMessageType === 'GOODS'">[商品链接]</span>
|
||||
<span v-if="item.lastMessageType === 'ORDER'">[订单链接]</span>
|
||||
</div>
|
||||
@@ -156,6 +160,7 @@ import {
|
||||
getCurrentInstance,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
watch,
|
||||
} from "vue";
|
||||
@@ -174,6 +179,7 @@ import {
|
||||
} from "@/api/chat";
|
||||
import { ServeDeleteContact, ServeEditContactRemark } from "@/api/contacts";
|
||||
import { beautifyTime } from "@/utils/functions";
|
||||
import { textReplaceEmoji } from "@/utils/emojis";
|
||||
import { formatTalkItem, findTalkIndex, getCacheIndexName } from "@/utils/talk";
|
||||
import { setToken } from "@/utils/auth";
|
||||
import { syncImTokenFromRoute } from "@/utils/im-mode";
|
||||
@@ -243,7 +249,9 @@ const unreadNum = computed(() => talkStore.unreadNum);
|
||||
const talkNum = computed(() => talkStore.talkNum);
|
||||
|
||||
// 原 vuex mapState
|
||||
const face = computed(() => userStore.face);
|
||||
// 注意:不能命名为 face——会与全局注册的 <face> 组件绑定冲突,
|
||||
// 模板里的 <face> 标签会被解析成这个 computed 的值(头像 base64 字符串)当作标签名渲染
|
||||
const userFace = computed(() => userStore.face);
|
||||
const name = computed(() => userStore.name);
|
||||
const talks = computed(() => talkStore.items);
|
||||
const id = computed(() => userStore.id);
|
||||
@@ -655,10 +663,11 @@ watch(talkItems, (val) => {
|
||||
});
|
||||
|
||||
// 搜索用户的时候 根据当前用户表进行模糊搜索
|
||||
watch(input, (val, oldVal) => {
|
||||
console.log(val, oldVal);
|
||||
watch(input, (val) => {
|
||||
if (val) {
|
||||
let str = ["", ...val, ""].join(".*");
|
||||
// 转义正则元字符,避免输入 [ 等字符时构造 RegExp 抛异常
|
||||
const keyword = val.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
let str = ["", ...keyword, ""].join(".*");
|
||||
let reg = new RegExp(str);
|
||||
userTalkItem.value = talkItems.value.filter((item) =>
|
||||
reg.test(item.name)
|
||||
@@ -703,8 +712,10 @@ onBeforeRouteUpdate((to, from, next) => {
|
||||
// 原 beforeCreate
|
||||
syncImTokenFromRoute(route, setToken);
|
||||
|
||||
// 原 async created(保持 await 顺序,但用 IIFE 避免 setup 变为异步)
|
||||
(async () => {
|
||||
// 原 async created。不能在 setup 同步执行:全局 mixin 的方法(initialize/
|
||||
// loadUserSetting)在 finishComponentSetup 的 applyOptions 阶段才挂到公共代理上,
|
||||
// 即 setup() 返回之后。故放到 onMounted 里执行。
|
||||
onMounted(async () => {
|
||||
await proxy.initialize();
|
||||
await proxy.loadUserSetting();
|
||||
// 初始化商品参数
|
||||
@@ -714,7 +725,7 @@ syncImTokenFromRoute(route, setToken);
|
||||
* 所以创建会话 并请求用户列表
|
||||
* 如果没有id说明当前商家登录 直接请求用户列表
|
||||
*/
|
||||
})();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.title = title;
|
||||
@@ -747,8 +758,20 @@ onBeforeUnmount(() => {
|
||||
flex-shrink: 0;
|
||||
height: 40px;
|
||||
|
||||
:deep(.el-input .el-input__inner ) {
|
||||
border-radius: 20px;
|
||||
:deep(.el-input__wrapper) {
|
||||
border-radius: 16px;
|
||||
background-color: #f5f6f7;
|
||||
box-shadow: none;
|
||||
transition: box-shadow 0.2s;
|
||||
|
||||
&.is-focus {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 0 1px #409eff inset;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-input__inner) {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1058,6 +1081,12 @@ onBeforeUnmount(() => {
|
||||
.talk-message {
|
||||
font-size: 12px;
|
||||
color: #8f959e;
|
||||
|
||||
:deep(img) {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.user-face {
|
||||
|
||||
@@ -20,6 +20,18 @@
|
||||
<el-input class="label-appkey" v-model="formValidate.wechatAppId" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="提现规则说明" prop="withdrawRule">
|
||||
<div style="display: flex; flex-direction: column; width: 500px;">
|
||||
<el-input
|
||||
v-model="formValidate.withdrawRule"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="请输入提现规则说明(需包含可提现额度、每日提现次数、提现时间、到账时间等,以满足微信审核要求)"
|
||||
/>
|
||||
<div class="rule-tip">展示在移动端/小程序提现页面底部,用于满足微信审核对提现规则公示的要求。</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<div class="label-btns">
|
||||
<el-button type="primary" @click="submit('formValidateRef')">保存</el-button>
|
||||
</div>
|
||||
@@ -32,6 +44,13 @@ import { ElMessage } from "element-plus";
|
||||
import { setSetting } from "@/api/index";
|
||||
import { handleSubmit } from "./validate";
|
||||
|
||||
const DEFAULT_WITHDRAW_RULE = `1. 提现额度:单笔最低提现金额以页面展示为准,单日累计最高提现 5000 元;
|
||||
2. 提现次数:每个用户每日最多可申请提现 1 次;
|
||||
3. 提现时间:支持全天 24 小时随时发起提现申请;
|
||||
4. 到账时间:提交申请后预计 1~3 个工作日内审核并到账至您的微信零钱;
|
||||
5. 手续费说明:提现免收手续费;
|
||||
6. 如有其他疑问或遇到提现未到账,请联系在线客服协助处理。`;
|
||||
|
||||
const props = defineProps(["res", "type"]);
|
||||
|
||||
// handleSubmit(that, name) 通过 that.$refs[name] 取表单实例
|
||||
@@ -64,29 +83,49 @@ function setupSetting() {
|
||||
});
|
||||
}
|
||||
function init() {
|
||||
result.value = JSON.parse(props.res);
|
||||
result.value = JSON.parse(props.res || "{}");
|
||||
Object.keys(result.value).map((item) => {
|
||||
result.value[item] += "";
|
||||
});
|
||||
formValidate.value = { ...result.value };
|
||||
if (!formValidate.value.withdrawRule) {
|
||||
formValidate.value.withdrawRule = DEFAULT_WITHDRAW_RULE;
|
||||
}
|
||||
Object.keys(formValidate.value).forEach((item) => {
|
||||
ruleValidate[item] = [
|
||||
{
|
||||
required: true,
|
||||
message: "请填写必填项",
|
||||
trigger: "blur",
|
||||
},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value < 0) {
|
||||
callback(new Error("不能输入负数!"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
if (item === "minPrice") {
|
||||
ruleValidate[item] = [
|
||||
{
|
||||
required: true,
|
||||
message: "请填写必填项",
|
||||
trigger: "blur",
|
||||
},
|
||||
trigger: "change",
|
||||
},
|
||||
];
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (Number(value) < 0) {
|
||||
callback(new Error("不能输入负数!"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: "change",
|
||||
},
|
||||
];
|
||||
} else if (item === "withdrawRule") {
|
||||
ruleValidate[item] = [
|
||||
{
|
||||
required: false,
|
||||
trigger: "blur",
|
||||
},
|
||||
];
|
||||
} else {
|
||||
ruleValidate[item] = [
|
||||
{
|
||||
required: true,
|
||||
message: "请填写必填项",
|
||||
trigger: "blur",
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -96,7 +135,13 @@ init();
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
:deep(.el-form-item__content){
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.rule-tip {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
line-height: 1.5;
|
||||
margin-top: 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user