Merge branch 'fix/im-vue3-migration'

This commit is contained in:
田香琪
2026-07-22 18:10:04 +08:00
55 changed files with 672 additions and 378 deletions

View File

@@ -3,7 +3,7 @@
</template>
<script>
import { Message } from "@/utils/message";
import { Modal } from "@/utils/message";
import { getIMDetail } from "@/api/common";
import Storage from "@/plugins/storage";
import { getMemberMsg } from "@/api/login";
@@ -15,28 +15,52 @@ export default {
};
},
methods: {
promptLogin() {
Modal.confirm({
title: "温馨提示",
content: "请登录后执行此操作",
okText: "立即登录",
cancelText: "取消",
onOk: () => {
this.$router.push({
path: "/login",
query: {
rePath: this.$route.path,
query: JSON.stringify(this.$route.query || {}),
},
});
},
});
},
// 跳转im客服
async IMService(id, goodsId, skuId) {
if (!Storage.getItem("accessToken")) {
this.promptLogin();
return;
}
// 获取访问Token
let accessToken = Storage.getItem("accessToken");
await this.getIMDetailMethods();
const userInfo = await getMemberMsg();
if (userInfo.success) {
//携带商品Id,在IM可以发送商品信息
if(goodsId && skuId){
window.open(
this.IMLink + "?token=" + accessToken + "&id=" + (id || this.storeMsg.storeId) + "&goodsId=" + goodsId + "&skuId=" + skuId
);
}else{
window.open(
this.IMLink + "?token=" + accessToken + "&id=" + (id || this.storeMsg.storeId)
);
try {
await this.getIMDetailMethods();
const userInfo = await getMemberMsg();
if (!userInfo.success) {
this.promptLogin();
return;
}
} else {
Message.error("请登录后再联系客服");
return;
const accessToken = Storage.getItem("accessToken");
const storeId = id || this.storeMsg?.storeId;
const baseUrl = `${this.IMLink}?token=${accessToken}&id=${storeId}`;
if (goodsId && skuId) {
window.open(`${baseUrl}&goodsId=${goodsId}&skuId=${skuId}`);
} else {
window.open(baseUrl);
}
} catch {
// token 过期等场景由 request 拦截器处理;未登录时避免未捕获异常
if (!Storage.getItem("accessToken")) {
this.promptLogin();
}
}
},
// 获取im信息

View File

@@ -284,7 +284,7 @@ export default {
// 跳转店铺首页
goShopPage(id) {
let routeUrl = this.$router.resolve({
path: "/Merchant",
path: "/merchant",
query: { id },
});
window.open(routeUrl.href, "_blank");

View File

@@ -177,7 +177,7 @@ export default {
let url
if (this.params.type === 'STORE') {
url = this.$router.resolve({
path: '/Merchant',
path: '/merchant',
query: { 'id': storeId }
})
} else {

View File

@@ -198,7 +198,7 @@ export default {
// 跳转店铺首页
shopPage(id) {
let routeUrl = this.$router.resolve({
path: '/Merchant',
path: '/merchant',
query: {id: id}
});
window.open(routeUrl.href, '_blank');

View File

@@ -265,7 +265,7 @@ export default {
// 跳转店铺首页
shopPage (id) {
let routeUrl = this.$router.resolve({
path: '/Merchant',
path: '/merchant',
query: { id: id }
});
window.open(routeUrl.href, '_blank');

View File

@@ -424,7 +424,7 @@ export default {
// 跳转店铺首页
shopPage(id) {
let routeUrl = this.$router.resolve({
path: "/Merchant",
path: "/merchant",
query: { id: id },
});
window.open(routeUrl.href, "_blank");

View File

@@ -71,7 +71,7 @@ export default {
// 跳转店铺首页
shopPage(id) {
let routeUrl = this.$router.resolve({
path: "/Merchant",
path: "/merchant",
query: { id: id },
});
window.open(routeUrl.href, "_blank");

View File

@@ -794,7 +794,7 @@ export default {
// 跳转店铺首页
goShopPage(id) {
let routeUrl = this.$router.resolve({
path: "/Merchant",
path: "/merchant",
query: { id: id },
});
window.open(routeUrl.href, "_blank");

View File

@@ -0,0 +1,81 @@
const fs = require("fs");
const path = require("path");
const root = path.join(__dirname, "../src");
const pairs = [
[/<i class="el-icon-close"/g, '<legacy-el-icon name="el-icon-close"'],
[/<i class="close-btn el-icon-close"/g, '<legacy-el-icon name="el-icon-close" class="close-btn"'],
[/<i class="el-icon-loading"/g, '<legacy-el-icon name="el-icon-loading"'],
[/<i class="el-icon-bottom"/g, '<legacy-el-icon name="el-icon-bottom"'],
[/<i class="el-icon-chat-dot-round"/g, '<legacy-el-icon name="el-icon-chat-dot-round"'],
[/<i class="el-icon-search"/g, '<legacy-el-icon name="el-icon-search"'],
[/<i class="el-icon-top"/g, '<legacy-el-icon name="el-icon-top"'],
[/<i class="el-icon-arrow-down"/g, '<legacy-el-icon name="el-icon-arrow-down"'],
[/<i class="el-icon-time"/g, '<legacy-el-icon name="el-icon-time"'],
[/<i class="el-icon-setting"/g, '<legacy-el-icon name="el-icon-setting"'],
[/<i class="el-icon-circle-close"/g, '<legacy-el-icon name="el-icon-circle-close"'],
[/<i class="el-icon-position"/g, '<legacy-el-icon name="el-icon-position"'],
[/<i class="el-icon-delete"/g, '<legacy-el-icon name="el-icon-delete"'],
[/<i class="el-icon-picture"/g, '<legacy-el-icon name="el-icon-picture"'],
[/<i class="el-icon-caret-left"/g, '<legacy-el-icon name="el-icon-caret-left"'],
[/<i class="el-icon-caret-right"/g, '<legacy-el-icon name="el-icon-caret-right"'],
[/<i class="el-icon-plus"><\/i>/g, '<legacy-el-icon name="el-icon-plus" />'],
[/<i class="el-icon-headset"/g, '<legacy-el-icon name="el-icon-headset"'],
[/<i class="el-icon-picture-outline-round"/g, '<legacy-el-icon name="el-icon-picture-outline-round"'],
[/<i class="el-icon-folder"/g, '<legacy-el-icon name="el-icon-folder"'],
[/<i class="el-icon-folder-opened"/g, '<legacy-el-icon name="el-icon-folder-opened"'],
[/<i class="el-icon-s-data"/g, '<legacy-el-icon name="el-icon-s-data"'],
[/<i class="el-icon-chat-line-round"/g, '<legacy-el-icon name="el-icon-chat-line-round"'],
[/<i class="el-icon-video-pause"/g, '<legacy-el-icon name="el-icon-video-pause"'],
[/<i class="el-icon-video-play"/g, '<legacy-el-icon name="el-icon-video-play"'],
[/<i class="el-icon-service"/g, '<legacy-el-icon name="el-icon-service"'],
[/<p class="tools"><i class="el-icon-close"/g, '<p class="tools"><legacy-el-icon name="el-icon-close"'],
[/<i class="el-icon-close close-btn"/g, '<legacy-el-icon name="el-icon-close" class="close-btn"'],
[/<i class="el-icon-plus" \/> 加好友/g, '<legacy-el-icon name="el-icon-plus" /> 加好友'],
[/<i class="el-icon-plus"><\/i> 添加选项/g, '<legacy-el-icon name="el-icon-plus" /> 添加选项'],
[
/<i @click="againSendMessage\(item\)" v-if="item.webSocketStatus" class="el-icon-refresh-left again main-color"><\/i>/g,
'<legacy-el-icon name="el-icon-refresh-left" class="again main-color" @click="againSendMessage(item)" v-if="item.webSocketStatus" />',
],
[
/<i class="el-icon-success" :class="\{ selected: verifyMultiSelect\(item.id\) \}"\s*@click="triggerMultiSelect\(item.id\)" \/>/g,
'<legacy-el-icon name="el-icon-success" :class="{ selected: verifyMultiSelect(item.id) }" @click="triggerMultiSelect(item.id)" />',
],
[/icon="el-icon-microphone"/g, ":icon=\"$legacyIcon('el-icon-microphone')\""],
[/icon="el-icon-video-pause"/g, ":icon=\"$legacyIcon('el-icon-video-pause')\""],
[/icon="el-icon-video-play"/g, ":icon=\"$legacyIcon('el-icon-video-play')\""],
[/icon="el-icon-upload"/g, ":icon=\"$legacyIcon('el-icon-upload')\""],
[/icon="el-icon-refresh"/g, ":icon=\"$legacyIcon('el-icon-refresh')\""],
[/icon="el-icon-refresh-left"/g, ":icon=\"$legacyIcon('el-icon-refresh-left')\""],
[/icon="el-icon-refresh-right"/g, ":icon=\"$legacyIcon('el-icon-refresh-right')\""],
[/prefix-icon="el-icon-search"/g, ":prefix-icon=\"$legacyIcon('el-icon-search')\""],
[/size="mini"/g, 'size="small"'],
[/size='mini'/g, "size='small'"],
[/size="medium"/g, 'size="default"'],
];
function walk(dir, files = []) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) walk(full, files);
else if (full.endsWith(".vue")) files.push(full);
}
return files;
}
let changed = 0;
for (const file of walk(root)) {
let content = fs.readFileSync(file, "utf8");
let next = content;
for (const [pattern, replacement] of pairs) {
next = next.replace(pattern, replacement);
}
if (next !== content) {
fs.writeFileSync(file, next);
console.log("updated:", path.relative(root, file));
changed++;
}
}
console.log("files changed:", changed);

View File

@@ -17,31 +17,35 @@ export const ServeGetUserDetail = (memberId) => {
};
// 获取店铺相关设置信息
export const ServeGetStoreDetail = (storeId) => {
return get(`${config.BASE_BUYER}/buyer/store/store/store/${storeId}`);
export const ServeGetStoreDetail = (storeId, options = {}) => {
return get(`${config.BASE_BUYER}/buyer/store/store/store/${storeId}`, {}, options);
};
// 获取用户历史足迹
export const ServeGetFootPrint = (params) => {
return get(`${config.BASE_BUYER}/buyer/member/footprint`, params);
export const ServeGetFootPrint = (params, options = {}) => {
return get(`${config.BASE_BUYER}/buyer/member/footprint`, params, options);
};
// 商家获取用户历史足迹
export const ServeStoreGetFootPrint = (params) => {
return get(`${config.BASE_SELLER}/store/member/footprint`, params);
export const ServeStoreGetFootPrint = (params, options = {}) => {
return get(`${config.BASE_SELLER}/store/member/footprint`, params, options);
};
// 获取用户订单列表信息
export const ServeGetOrderPrint = (params) => {
return get(`${config.BASE_BUYER}/buyer/order/order`, params);
export const ServeGetOrderPrint = (params, options = {}) => {
return get(`${config.BASE_BUYER}/buyer/order/order`, params, options);
};
// 商家获取用户订单列表信息
export const ServeStoreGetOrderPrint = (params) => {
return get(`${config.BASE_SELLER}/store/order/order`, params);
export const ServeStoreGetOrderPrint = (params, options = {}) => {
return get(`${config.BASE_SELLER}/store/order/order`, params, options);
};
// 获取商品信息
export const ServeGetGoodsDetail = (data) => {
return get(`${config.BASE_BUYER}/buyer/goods/goods/sku/${data.goodsId}/${data.skuId}`);
export const ServeGetGoodsDetail = (data, options = {}) => {
return get(
`${config.BASE_BUYER}/buyer/goods/goods/sku/${data.goodsId}/${data.skuId}`,
{},
options
);
};

5
im/src/assets/images.js Normal file
View File

@@ -0,0 +1,5 @@
import defaultAvatar from "@/assets/image/detault-avatar.jpg";
import defaultUserBanner from "@/assets/image/default-user-banner.png";
import noOncallImage from "@/assets/image/no-oncall.6b776fcf.png";
export { defaultAvatar, defaultUserBanner, noOncallImage };

View File

@@ -5,11 +5,11 @@
<el-header class="header no-padding" height="50px">
<div class="tools">
<span>选择编程语言:&nbsp;&nbsp;</span>
<el-select v-model="language" size="mini" filterable placeholder="语言类型" :disabled="!editMode">
<el-select v-model="language" size="small" filterable placeholder="语言类型" :disabled="!editMode">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<i class="el-icon-close close-btn" @click="close" />
<legacy-el-icon name="el-icon-close" class="close-btn" @click="close" />
<i class="iconfont icon-full-screen" :class="{
'icon-tuichuquanping': isFullScreen,
'icon-quanping ': !isFullScreen,

View File

@@ -4,7 +4,7 @@
<el-header class="no-padding header" height="60px">
<p>会话记录 ({{ records.length }})</p>
<p class="tools">
<i class="el-icon-close" @click="close" />
<legacy-el-icon name="el-icon-close" @click="close" />
</p>
</el-header>
<el-main class="no-padding main" v-loading="loading">

View File

@@ -13,7 +13,7 @@
:class="fullscreen ? 'icon-tuichuquanping' : 'icon-quanping'"
@click="fullscreen = !fullscreen"
/>
<i class="el-icon-close" @click="$emit('close')" />
<legacy-el-icon name="el-icon-close" @click="$emit('close')" />
</p>
</el-header>
@@ -24,7 +24,7 @@
@click="triggerBroadside"
/>
<div class="search-box no-select">
<i class="el-icon-search" />
<legacy-el-icon name="el-icon-search" />
<input
v-model="search.keyword"
type="text"
@@ -196,11 +196,11 @@
<!-- 数据加载栏 -->
<div v-show="records.loadStatus == 1" class="load-button blue">
<i class="el-icon-loading" />
<legacy-el-icon name="el-icon-loading" />
<span>加载数据中...</span>
</div>
<div v-show="records.loadStatus == 0" class="load-button">
<i class="el-icon-arrow-down" />
<legacy-el-icon name="el-icon-arrow-down" />
<span @click="loadChatRecord">加载更多...</span>
</div>
</el-scrollbar>

View File

@@ -2,9 +2,9 @@
<div class="audio-message">
<div class="videodisc">
<div class="disc" :class="{ play: isPlay }" @click="toPlay">
<i v-if="loading" class="el-icon-loading" />
<i v-else-if="isPlay" class="el-icon-video-pause" />
<i v-else class="el-icon-video-play" />
<legacy-el-icon v-if="loading" name="el-icon-loading" />
<legacy-el-icon v-else-if="isPlay" name="el-icon-video-pause" />
<legacy-el-icon v-else name="el-icon-video-play" />
<audio
ref="audio"
type="audio/mp3"
@@ -17,7 +17,7 @@
</div>
<div class="detail">
<div class="text">
<i class="el-icon-service" />
<legacy-el-icon name="el-icon-service" />
<span>{{ getCurrDuration }} / {{ getTotalDuration }}</span>
</div>
<div class="process">

View File

@@ -7,10 +7,14 @@
'full-screen': fullscreen,
}"
>
<legacy-el-icon
v-if="fullscreen"
name="el-icon-close"
@click="fullscreen = !fullscreen"
/>
<i
:class="
fullscreen ? 'el-icon-close' : 'iconfont icon-tubiao_chakangongyi'
"
v-else
class="iconfont icon-tubiao_chakangongyi"
@click="fullscreen = !fullscreen"
/>
<pre class="lum-scrollbar" v-html="formatCode(code, lang)" />

View File

@@ -9,7 +9,7 @@
</div>
<div class="tools flex-center">
<span class="flex-center pointer">
<i class="el-icon-plus" /> 加好友
<legacy-el-icon name="el-icon-plus" /> 加好友
</span>
</div>
</div>

View File

@@ -5,16 +5,13 @@
<div class="vheader">
<p>
{{ answer_mode == 1 ? "[多选投票]" : "[单选投票]" }}
<i
<legacy-el-icon
v-show="is_vote"
class="pointer"
:class="{
'el-icon-loading': refresh,
'el-icon-refresh': !refresh,
}"
:name="refresh ? 'el-icon-loading' : 'el-icon-refresh'"
title="刷新投票结果"
@click="loadRefresh"
></i>
/>
</p>
<p>{{ title }}</p>
</div>

View File

@@ -17,17 +17,29 @@
</template>
<script>
import { ServeGetStoreDetail, ServeGetUserDetail, ServeGetFootPrint, ServeGetOrderPrint, ServeGetGoodsDetail, ServeStoreGetFootPrint,ServeStoreGetOrderPrint } from '@/api/user'
import {
ServeGetStoreDetail,
ServeGetFootPrint,
ServeGetOrderPrint,
ServeGetGoodsDetail,
ServeStoreGetFootPrint,
ServeStoreGetOrderPrint,
} from "@/api/user";
import StoreDetail from "@/components/chat/panel/template/storeDetail.vue";
import FootPrint from "@/components/chat/panel/template/footPrint.vue";
import GoodsLink from "@/components/chat/panel/template/goodsLink.vue";
import SocketInstance from "@/im-server/socket-instance";
import { mapState, mapGetters } from "vuex";
import { getToken } from "@/utils/auth";
import { isBuyerImMode } from "@/utils/im-mode";
const SIDE_REQUEST_OPTIONS = { skipAuthDialog: true };
export default {
components: {
StoreDetail,
FootPrint,
GoodsLink
GoodsLink,
},
props: {
toUser: {
@@ -36,7 +48,7 @@ export default {
},
id: {
type: String,
default: '',
default: "",
},
goodsParams: {
type: Object,
@@ -48,167 +60,173 @@ export default {
...mapState({
index_name: (state) => state.dialogue.index_name,
}),
isBuyerImMode() {
return isBuyerImMode(this.$route);
},
},
watch:{
toUser(){
localStorage.setItem('storeFlag', this.toUser.storeFlag)
this.footPrintList = []
this.orderPrintList = []
this.footPrintParams.pageNumber = 1
watch: {
toUser() {
this.syncStoreFlagCache();
this.resetSidePanelData();
if (this.toUser.storeFlag) {
this.getStoreDetail()
}
// else {
// this.getMemberDetail()
// }
this.getFootPrint()
if (this.goodsParams && this.toUser.storeFlag == true) {
this.getGoodsDetail()
this.getStoreDetail();
}
}
this.getFootPrint();
if (this.goodsParams && this.toUser.storeFlag) {
this.getGoodsDetail();
}
},
},
data () {
data() {
return {
activeName: 'history',
storeInfo: {}, //店铺信息
memberInfo: {}, //会员信息
activeName: "history",
storeInfo: {},
memberInfo: {},
footPrintParams: {
pageSize: 20,
pageNumber: 1,
memberId: '',
storeId: '',
memberId: "",
storeId: "",
},
goodsDetail: {},
footPrintList: [], // 商品
orderPrintList: []// 订单
}
footPrintList: [],
orderPrintList: [],
};
},
mounted () {
localStorage.setItem('storeFlag', this.toUser.storeFlag)
mounted() {
this.syncStoreFlagCache();
if (this.toUser.storeFlag) {
this.getStoreDetail()
}
// else {
// this.getMemberDetail()
// }
this.getFootPrint()
if (this.goodsParams && this.toUser.storeFlag == true) {
this.getGoodsDetail()
this.getStoreDetail();
}
this.getFootPrint();
if (this.goodsParams && this.toUser.storeFlag) {
this.getGoodsDetail();
}
},
methods: {
getStoreDetail () {
ServeGetStoreDetail(this.toUser.userId).then(res => {
if (res.success) {
this.storeInfo = res.result
}
})
syncStoreFlagCache() {
localStorage.setItem(
"storeFlag",
this.isBuyerImMode ? "true" : "false"
);
},
loadMoreFootPrint (e) {
//触底再次调接口
this.footPrintParams.pageNumber++
this.getFootPrint()
resetSidePanelData() {
this.footPrintList = [];
this.orderPrintList = [];
this.footPrintParams.pageNumber = 1;
},
handleClick () { },
// getMemberDetail () {
// ServeGetUserDetail(this.toUser.userId).then(res => {
// if (res.success) {
// this.memberInfo = res.result
// }
// })
// },
getGoodsDetail () {
// 检查必要参数是否存在
getStoreDetail() {
ServeGetStoreDetail(this.toUser.userId, SIDE_REQUEST_OPTIONS)
.then((res) => {
if (res.success) {
this.storeInfo = res.result;
}
})
.catch(() => {});
},
loadMoreFootPrint() {
this.footPrintParams.pageNumber++;
this.getFootPrint();
},
handleClick() {},
getGoodsDetail() {
if (!this.toUser.storeFlag) {
return
return;
}
if (!this.goodsParams || !this.goodsParams.goodsId) {
console.warn('getGoodsDetail: goodsParams 或 goodsId 参数缺失')
return
return;
}
ServeGetGoodsDetail(this.goodsParams).then(res => {
if (res.success) {
this.goodsDetail = res.result.data
}
}).catch(error => {
console.error('获取商品详情失败:', error)
})
ServeGetGoodsDetail(this.goodsParams, SIDE_REQUEST_OPTIONS)
.then((res) => {
if (res.success) {
this.goodsDetail = res.result.data;
}
})
.catch(() => {});
},
normalizeFootPrintRecords(records = []) {
const goodsId = this.goodsParams?.goodsId;
return records
.filter((item) => item != null)
.filter((item) => !goodsId || item.goodsId !== goodsId)
.map((item) => ({
...item,
btnHide: localStorage.getItem(item.goodsId) ? 0 : 1,
}));
},
normalizeOrderRecords(records = []) {
return records.map((item) => ({
...item,
btnHide: 1,
}));
},
getFootPrint() {
if (this.toUser.storeFlag) {
this.footPrintParams.memberId = this.id
this.footPrintParams.storeId = this.toUser.userId
ServeGetFootPrint(this.footPrintParams).then(res => {
res.result.records=res.result.records.filter((item)=>{
return item!=null
})
res.result.records.forEach((item, index) => {
if (localStorage.getItem(item.goodsId)) {
item.btnHide = 0
} else {
item.btnHide = 1
}
if (item.goodsId === this.goodsParams.goodsId) {
res.result.records.splice(index, 1)
}
});
this.footPrintList.push(...res.result.records)
})
// 订单列表
ServeGetOrderPrint(this.footPrintParams).then((res) => {
if (res.code == 200) {
res.result.records.forEach((item) => {
this.orderPrintList.push({
...item,
btnHide: 1
})
})
}
})
if (!getToken()) {
return;
}
if (!this.toUser?.userId || !this.id) {
return;
}
if (this.isBuyerImMode) {
this.footPrintParams.memberId = this.id;
this.footPrintParams.storeId = this.toUser.userId;
this.fetchBuyerSideData();
} else {
this.footPrintParams.memberId = this.toUser.userId
this.footPrintParams.storeId = this.id
ServeStoreGetFootPrint(this.footPrintParams).then(res => {
res.result.records=res.result.records.filter((item)=>{
return item!=null
})
res.result.records.forEach((item, index) => {
if (localStorage.getItem(item.goodsId)) {
item.btnHide = 0
} else {
item.btnHide = 1
}
if (item.goodsId === this.goodsParams.goodsId) {
res.result.records.splice(index, 1)
}
});
this.footPrintList.push(...res.result.records)
})
ServeStoreGetOrderPrint(this.footPrintParams).then((res) => {
if (res.code == 200) {
res.result.records.forEach((item) => {
this.orderPrintList.push({
...item,
btnHide: 1
})
})
}
})
this.footPrintParams.memberId = this.toUser.userId;
this.footPrintParams.storeId = this.id;
this.fetchStoreSideData();
}
},
fetchBuyerSideData() {
ServeGetFootPrint(this.footPrintParams, SIDE_REQUEST_OPTIONS)
.then((res) => {
if (res?.result?.records) {
this.footPrintList.push(
...this.normalizeFootPrintRecords(res.result.records)
);
}
})
.catch(() => {});
// 发送消息回调事件
submitSendMessage (record, context, messageType) {
ServeGetOrderPrint(this.footPrintParams, SIDE_REQUEST_OPTIONS)
.then((res) => {
if (res?.code === 200 && res?.result?.records) {
this.orderPrintList.push(
...this.normalizeOrderRecords(res.result.records)
);
}
})
.catch(() => {});
},
fetchStoreSideData() {
ServeStoreGetFootPrint(this.footPrintParams, SIDE_REQUEST_OPTIONS)
.then((res) => {
if (res?.result?.records) {
this.footPrintList.push(
...this.normalizeFootPrintRecords(res.result.records)
);
}
})
.catch(() => {});
ServeStoreGetOrderPrint(this.footPrintParams, SIDE_REQUEST_OPTIONS)
.then((res) => {
if (res?.code === 200 && res?.result?.records) {
this.orderPrintList.push(
...this.normalizeOrderRecords(res.result.records)
);
}
})
.catch(() => {});
},
submitSendMessage(record, context, messageType) {
SocketInstance.emit("event_talk", record);
this.$store.commit("UPDATE_TALK_ITEM", {
index_name: this.index_name,
draft_text: "",
});
/**
* 插入数据
*/
const insterChat = {
createTime: this.formateDateAndTimeToString(new Date()),
fromUser: this.id,
@@ -219,11 +237,8 @@ export default {
float: "right",
};
// 插入对话记录
this.$store.commit("PUSH_DIALOGUE", insterChat);
// 获取聊天面板元素节点
let el = document.getElementById("lumenChatPanel");
// 判断的滚动条是否在底部
let isBottom =
Math.ceil(el.scrollTop) + el.clientHeight >= el.scrollHeight;
@@ -233,14 +248,13 @@ export default {
});
} else {
this.$store.commit("SET_TLAK_UNREAD_MESSAGE", {
content: content,
content: context,
nickname: record.name,
});
}
},
formateDateAndTimeToString (date) {
formateDateAndTimeToString(date) {
var hours = date.getHours();
var mins = date.getMinutes();
var secs = date.getSeconds();
@@ -254,7 +268,7 @@ export default {
);
},
formatDateToString (date) {
formatDateToString(date) {
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
@@ -262,15 +276,11 @@ export default {
if (day < 10) day = "0" + day;
return year + "-" + month + "-" + day;
},
}
}
},
};
</script>
<style scoped lang="less">
// :deep(.el-tabs__nav.is-top ) {
// }
:deep(.el-tabs__nav ) {
height: 60px;
line-height: 60px;

View File

@@ -23,7 +23,7 @@
<div class="module right-module" >
<el-tooltip content="历史消息" placement="top">
<p v-show="params.is_robot == 0">
<i class="el-icon-time" @click="triggerEvent('history')" />
<legacy-el-icon name="el-icon-time" @click="triggerEvent('history')" />
</p>
</el-tooltip>
<el-tooltip content="群公告" placement="top">
@@ -33,7 +33,7 @@
</el-tooltip>
<el-tooltip content="群设置" placement="top">
<p v-show="params.talk_type == 2">
<i class="el-icon-setting" @click="triggerEvent('setting')" />
<legacy-el-icon name="el-icon-setting" @click="triggerEvent('setting')" />
</p>
</el-tooltip>
</div>

View File

@@ -9,25 +9,25 @@
class="multi-icon pointer"
@click="$emit('event', 'merge_forward')"
>
<i class="el-icon-position" />
<legacy-el-icon name="el-icon-position" />
</div>
<p>合并转发</p>
</div>
<div class="btn-group">
<div class="multi-icon pointer" @click="$emit('event', 'forward')">
<i class="el-icon-position" />
<legacy-el-icon name="el-icon-position" />
</div>
<p>逐条转发</p>
</div>
<div class="btn-group">
<div class="multi-icon pointer" @click="$emit('event', 'delete')">
<i class="el-icon-delete" />
<legacy-el-icon name="el-icon-delete" />
</div>
<p>批量删除</p>
</div>
<div class="btn-group">
<div class="multi-icon pointer" @click="$emit('event', 'close')">
<i class="el-icon-close" />
<legacy-el-icon name="el-icon-close" />
</div>
<p>关闭</p>
</div>

View File

@@ -9,10 +9,10 @@
<!-- 数据加载状态栏 -->
<div class="loading-toolbar">
<span v-if="loadRecord.status == 0" class="color-blue">
<i class="el-icon-loading" /> 正在加载数据中...
<legacy-el-icon name="el-icon-loading" /> 正在加载数据中...
</span>
<span v-if="loadRecord.status == 1" class="pointer color-blue" @click="loadChatRecords">
<i class="el-icon-bottom" /> 查看更多消息...
<legacy-el-icon name="el-icon-bottom" /> 查看更多消息...
</span>
<span v-if="loadRecord.status == 2"> 没有更多消息了... </span>
</div>
@@ -39,8 +39,7 @@
'checkbox-border': multiSelect.isOpen === true,
}">
<aside v-show="multiSelect.isOpen" class="checkbox-column">
<i class="el-icon-success" :class="{ selected: verifyMultiSelect(item.id) }"
@click="triggerMultiSelect(item.id)" />
<legacy-el-icon name="el-icon-success" :class="{ selected: verifyMultiSelect(item.id) }" @click="triggerMultiSelect(item.id)" />
</aside>
<aside class="avatar-column">
<div class="avatar-box" v-if="item.float == 'right'">
@@ -59,7 +58,7 @@
{{ unixToDate(item.createTime, "MM月dd日 hh:mm") }}
</span>
<div class="flex flex-a-c">
<i @click="againSendMessage(item)" v-if="item.webSocketStatus" class="el-icon-refresh-left again main-color"></i>
<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;"
class="text-message" :class="{
@@ -122,7 +121,7 @@
</span>
<div class="order-status">
<el-tag
size="mini"
size="small"
:type="item.text.orderStatus == 'CANCELLED' || item.text.orderStatus == 'UNPAID' || item.text.orderStatus == ' TAKE' ? 'info' : 'danger'">{{
item.text.orderStatus == 'CANCELLED' ? '已取消' : item.text.orderStatus == 'UNPAID' ? '未付款' :
item.text.orderStatus ==
@@ -156,7 +155,7 @@
<!-- 新消息气泡 -->
<div v-show="tipsBoard && unreadMessage.num" class="talk-bubble pointer no-select"
@click="talkPanelScrollBottom">
<i class="el-icon-chat-dot-round" />
<legacy-el-icon name="el-icon-chat-dot-round" />
<span>新消息({{ unreadMessage.num }})</span>
<span>
&nbsp;#{{ unreadMessage.nickname }}#
@@ -285,6 +284,9 @@ export default {
// 置底按钮是否显示
tipsBoard: false,
// 防止并发请求导致 loading 状态错乱
recordsRequestId: 0,
};
},
@@ -302,22 +304,15 @@ export default {
}),
},
watch: {
// 监听面板传递参数
params () {
this.loadRecord.minRecord = 0;
this.tipsBoard = false;
this.multiSelect = {
isOpen: false,
items: [],
mode: 0,
};
this.loadChatRecords();
// 监听面板传递参数(含首次挂载)
params: {
handler () {
this.resetRecordState();
this.loadChatRecords();
},
immediate: true,
},
},
mounted () {
this.loadChatRecords();
},
methods: {
parseTime,
sendTime: formatTime,
@@ -497,27 +492,49 @@ export default {
resetRecordState () {
this.loadRecord.minRecord = 0;
this.loadRecord.pageNumber = 0;
this.tipsBoard = false;
this.multiSelect = {
isOpen: false,
items: [],
mode: 0,
};
},
// 加载用户聊天详情信息
loadChatRecords () {
if (this.loadRecord.pageNumber === 0 || this.params.clickFlag) {
this.loadRecord.pageNumber = 1
this.params.clickFlag = false
} else {
this.loadRecord.pageNumber = this.loadRecord.pageNumber + 1
if (!this.params.talkId) {
this.loadRecord.status = 2;
return;
}
const isFirstPage = this.loadRecord.pageNumber === 0;
if (isFirstPage) {
this.loadRecord.pageNumber = 1;
} else {
this.loadRecord.pageNumber = this.loadRecord.pageNumber + 1;
}
const user_id = this.id;
const data = {
pageNumber: this.loadRecord.pageNumber,
pageSize: this.loadChatRecords.pageSize,
pageSize: this.loadRecord.pageSize,
talkId: this.params.talkId,
};
this.loadRecord.status = 0;
let el = document.getElementById('lumenChatPanel')
let scrollHeight = el.scrollHeight
const requestId = ++this.recordsRequestId;
let el = document.getElementById('lumenChatPanel');
let scrollHeight = el ? el.scrollHeight : 0;
ServeTalkRecords(data).then((res) => {
// 防止点击切换过快消息返回延迟,导致信息错误
// console.log("读取历史数据", res);
if (requestId !== this.recordsRequestId) return;
if (res.code !== 200 || !Array.isArray(res.result)) {
this.loadRecord.status = 2;
return;
}
const records = res.result.map((item) => {
let key = new Date().getTime();
item.float = "center";
@@ -525,28 +542,26 @@ export default {
item.float = item.fromUser == user_id ? "right" : "left";
}
if (item.messageType == 'GOODS') {
item.text = JSON.parse(item.text)
item.text = JSON.parse(item.text);
}
// if (item.messageType == 'MESSAGE"') {
// item.text = this.textReplaceEmoji(item.text)
// }
if (item.messageType == 'ORDER') {
item.text = JSON.parse(item.text)
item.text = JSON.parse(item.text);
}
return { ...item, [key]: key };
});
this.$store.commit("UNSHIFT_DIALOGUE", records);
records.length
? (this.loadRecord.status = 1)
: (this.loadRecord.status = 2);
this.loadRecord.status = records.length ? 1 : 2;
this.$nextTick(() => {
// if (data.record_id == 0 || !data.record_id) {
if (data.record_id == 0 || data.pageNumber == 1) {
el.scrollTop = el.scrollHeight
if (!el) return;
if (isFirstPage) {
el.scrollTop = el.scrollHeight;
} else {
el.scrollTop = el.scrollHeight - scrollHeight
el.scrollTop = el.scrollHeight - scrollHeight;
}
})
});
}).catch(() => {
if (requestId !== this.recordsRequestId) return;
this.loadRecord.status = 2;
});
},

View File

@@ -4,7 +4,7 @@
<el-tabs v-model="activeName" @tab-click="handleClick" :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" v-infinite-scroll="loadMore">
<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"
@@ -21,7 +21,7 @@
</div>
</div>
</div>
<div class="review"> <el-button v-if="item.btnHide == 1 && toUser.storeFlag" size="mini"
<div class="review"> <el-button v-if="item.btnHide == 1 && toUser.storeFlag" size="small"
@click.stop="submitSendGoodsMessage(item)" plain>发送</el-button></div>
</div>
</dd>
@@ -33,7 +33,7 @@
</el-tab-pane>
<el-tab-pane label="订单列表" name="orders">
<dl class='base-order-list' v-if='orderList.length'>
<dd v-for="(item, index) in orderList" v-infinite-scroll="loadMore" :key="index">
<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>
@@ -48,12 +48,12 @@
<div class="order-btn ">
<el-button class="store-button" v-if="item.btnHide == 1 && toUser.storeFlag"
size="mini" @click="submitSendOrderMessage(item, index)" plain>发送</el-button>
size="small" @click="submitSendOrderMessage(item, index)" plain>发送</el-button>
</div>
</div>
<div class="order-footer order-padding">
<span></span>
<el-tag size='mini' :type="col[item.orderStatus]">{{
<el-tag size="small" :type="col[item.orderStatus]">{{
item.orderStatus == 'STAY_PICKED_UP' ? '待自提'
: item.orderStatus == 'CANCELLED' ? '已取消' : item.orderStatus == 'UNPAID' ? '未付款' : item.orderStatus
==
@@ -108,12 +108,6 @@ export default {
toUser: (state) => state.user.toUser,
}),
},
components: {
"el-tag": Tag,
"el-button": button,
"el-tabs": Tabs,
"el-tab-pane": TabPane,
},
methods: {
//跳转订单列表
@@ -179,11 +173,11 @@ export default {
props: {
list: {
type: Array,
default: [],
default: () => [],
},
orderList: {
type: Array,
default: []
default: () => [],
},
},
mounted () {

View File

@@ -11,7 +11,7 @@
<span style="color: red;">{{ $filters.unitPrice(goodsDetail.price, '¥') }}</span>
</div>
<div v-if="hide">
<el-button class="store-button" type="danger" v-if="btnHide == 1 && toUser.storeFlag" size="mini"
<el-button class="store-button" type="danger" v-if="btnHide == 1 && toUser.storeFlag" size="small"
@click="submitSendMessage()" plain>发送</el-button>
</div>
</div>

View File

@@ -8,14 +8,14 @@
<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="mini">自营</el-tag>
<el-tag style="margin-left: 10px;" 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="mini"
<el-button class="store-button" @click="linkToStore(storeInfo.id)" size="small"
plain>进入店铺</el-button>
</div>
</div>

View File

@@ -18,24 +18,24 @@
<p class="tip-title">代码片段</p>
</li>
<li @click="recorder = true">
<i class="el-icon-headset" />
<legacy-el-icon name="el-icon-headset" />
<p class="tip-title">语音消息</p>
</li> -->
<!-- #TODO 发图片功能暂时隐藏 此处会涉及到token过期 -->
<!-- <li @click="$refs.restFile.click()">
<i class="el-icon-picture-outline-round" />
<legacy-el-icon name="el-icon-picture-outline-round" />
<p class="tip-title">图片</p>
</li> -->
<!-- <li @click="$refs.restFile2.click()">
<i class="el-icon-folder" />
<legacy-el-icon name="el-icon-folder" />
<p class="tip-title">附件</p>
</li>
<li @click="filesManager.isShow = true">
<i class="el-icon-folder-opened" />
<legacy-el-icon name="el-icon-folder-opened" />
<p class="tip-title">上传管理</p>
</li>
<li v-show="isGroupTalk" @click="vote.isShow = true">
<i class="el-icon-s-data" />
<legacy-el-icon name="el-icon-s-data" />
<p class="tip-title">发起投票</p>
</li> -->
@@ -87,7 +87,7 @@
@confirm="confirmCodeBlock" />
<!-- 文件上传管理器 -->
<MeEditorFileManage ref="filesManager" v-model:show="filesManager.isShow" />
<MeEditorFileManage ref="fileManageRef" v-model:show="filesManager.isShow" />
<MeEditorVote v-if="vote.isShow" @close="
() => {
@@ -133,7 +133,7 @@ export default {
},
watch: {
talkUser (n_index_name) {
this.$refs.filesManager.clear();
this.$refs.fileManageRef?.clear();
this.editorText = this.getDraftText(n_index_name);
},
},
@@ -251,7 +251,7 @@ export default {
this.filesManager.isShow = true;
this.$refs.restFile2.value = null;
this.$refs.filesManager.upload(file);
this.$refs.fileManageRef?.upload(file);
},
// 打开图片查看器

View File

@@ -43,7 +43,7 @@
class="emoji-item custom-emoji"
@click="$refs.fileCustomEmoji.click()"
>
<i class="el-icon-picture" />
<legacy-el-icon name="el-icon-picture" />
<span>自定义</span>
</div>
<div
@@ -64,7 +64,7 @@
class="toolbar-item prev-page"
@click="turnPage(1)"
>
<i class="el-icon-caret-left" />
<legacy-el-icon name="el-icon-caret-left" />
</div>
<div
v-for="(item, index) in showItems"
@@ -80,7 +80,7 @@
class="toolbar-item next-page"
@click="turnPage(2)"
>
<i class="el-icon-caret-right" />
<legacy-el-icon name="el-icon-caret-right" />
</div>
</div>
</el-footer> -->

View File

@@ -8,7 +8,7 @@
<p>
上传管理 <span v-show="total">({{ successNum }}/{{ total }})</span>
</p>
<i class="close-btn el-icon-close" @click="closeBox" />
<legacy-el-icon name="el-icon-close" class="close-btn" @click="closeBox" />
</el-header>
<el-main class="no-padding mian lum-scrollbar">

View File

@@ -4,7 +4,7 @@
<el-header class="no-padding header" height="50px">
<p>发送图片</p>
<p class="tools">
<i class="el-icon-close" @click="closeBox" />
<legacy-el-icon name="el-icon-close" @click="closeBox" />
</p>
</el-header>
@@ -21,7 +21,7 @@
<el-button
class="btn"
type="primary"
size="medium"
size="default"
:loading="loading"
@click="uploadImage"
>立即发送

View File

@@ -3,7 +3,7 @@
<el-container class="lum-dialog-box">
<el-header class="no-padding header no-select" height="50px">
<p>语音消息</p>
<p class="tools"><i class="el-icon-close" @click="closeBox" /></p>
<p class="tools"><legacy-el-icon name="el-icon-close" @click="closeBox" /></p>
</el-header>
<el-main class="no-padding mian">
@@ -52,45 +52,45 @@
<el-button
v-show="recorderStatus == 0"
type="primary"
size="mini"
size="small"
round
icon="el-icon-microphone"
:icon="$legacyIcon('el-icon-microphone')"
@click="startRecorder"
>开始录音
</el-button>
<el-button
v-show="recorderStatus == 1"
type="primary"
size="mini"
size="small"
round
icon="el-icon-video-pause"
:icon="$legacyIcon('el-icon-video-pause')"
@click="pauseRecorder"
>暂停录音
</el-button>
<el-button
v-show="recorderStatus == 2"
type="primary"
size="mini"
size="small"
round
icon="el-icon-microphone"
:icon="$legacyIcon('el-icon-microphone')"
@click="resumeRecorder"
>继续录音
</el-button>
<el-button
v-show="recorderStatus == 2"
type="primary"
size="mini"
size="small"
round
icon="el-icon-microphone"
:icon="$legacyIcon('el-icon-microphone')"
@click="stopRecorder"
>结束录音
</el-button>
<el-button
v-show="recorderStatus == 3 || recorderStatus == 6"
type="primary"
size="mini"
size="small"
round
icon="el-icon-video-play"
:icon="$legacyIcon('el-icon-video-play')"
@click="playRecorder"
>播放录音
</el-button>
@@ -100,9 +100,9 @@
recorderStatus == 3 || recorderStatus == 5 || recorderStatus == 6
"
type="primary"
size="mini"
size="small"
round
icon="el-icon-video-play"
:icon="$legacyIcon('el-icon-video-play')"
@click="startRecorder"
>重新录音
</el-button>
@@ -110,18 +110,18 @@
<el-button
v-show="recorderStatus == 4"
type="primary"
size="mini"
size="small"
round
icon="el-icon-video-pause"
:icon="$legacyIcon('el-icon-video-pause')"
@click="pausePlayRecorder"
>暂停播放
</el-button>
<el-button
v-show="recorderStatus == 5"
type="primary"
size="mini"
size="small"
round
icon="el-icon-video-play"
:icon="$legacyIcon('el-icon-video-play')"
@click="resumePlayRecorder"
>继续播放
</el-button>
@@ -131,7 +131,7 @@
recorderStatus == 3 || recorderStatus == 5 || recorderStatus == 6
"
type="primary"
size="mini"
size="small"
round
@click="submit"
>立即发送

View File

@@ -4,7 +4,7 @@
<el-header class="no-padding header" height="50px">
<p>系统表情</p>
<p class="tools">
<i class="el-icon-close" @click="closeBox" />
<legacy-el-icon name="el-icon-close" @click="closeBox" />
</p>
</el-header>
@@ -31,7 +31,7 @@
</el-main>
<el-footer class="footer" height="50px">
<el-button type="primary" size="medium" class="btn" @click="closeBox">
<el-button type="primary" size="default" class="btn" @click="closeBox">
关闭窗口
</el-button>
</el-footer>

View File

@@ -4,7 +4,7 @@
<el-header class="header no-select" height="60px">
<p>发起投票</p>
<p class="tools">
<i class="el-icon-close" @click="$emit('close')" />
<legacy-el-icon name="el-icon-close" @click="$emit('close')" />
</p>
</el-header>
<el-main class="main no-padding vote-from">
@@ -18,7 +18,7 @@
<div class="vote-title">投票主题</div>
<div>
<el-input
size="medium"
size="default"
clear="vote-input"
v-model.trim="title"
placeholder="请输入投票主题最多50字"
@@ -31,7 +31,7 @@
<div class="vote-options" v-for="(option, index) in options">
<div class="lbox">
<el-input
size="medium"
size="default"
clear="vote-input"
v-model.trim="option.value"
placeholder="请输入选项内容"
@@ -46,12 +46,12 @@
</el-input>
</div>
<div class="rbox">
<i class="el-icon-close" @click="removeOption(index)"></i>
<legacy-el-icon name="el-icon-close" @click="removeOption(index)" />
</div>
</div>
<h6 class="pointer add-option" @click="addOption">
<i class="el-icon-plus"></i> 添加选项
<legacy-el-icon name="el-icon-plus" /> 添加选项
</h6>
</div>
</el-main>

View File

@@ -8,6 +8,8 @@
</template>
<script>
import { noOncallImage } from "@/assets/images";
export default {
name: 'Empty',
props: {
@@ -17,7 +19,7 @@ export default {
},
src: {
type: String,
default: require('@/assets/image/no-oncall.6b776fcf.png'),
default: noOncallImage,
},
},
data() {

View File

@@ -0,0 +1,25 @@
<template>
<el-icon v-bind="$attrs">
<component :is="iconComponent" v-if="iconComponent" />
</el-icon>
</template>
<script>
import { resolveLegacyIcon } from "@/core/legacy-icon-map";
export default {
name: "LegacyElIcon",
inheritAttrs: false,
props: {
name: {
type: String,
default: "",
},
},
computed: {
iconComponent() {
return resolveLegacyIcon(this.name);
},
},
};
</script>

View File

@@ -2,7 +2,7 @@
<div>
<div class="abs-module" v-show="isShow">
<div class="abs-box">
<i class="el-icon-circle-close" @click="close" />
<legacy-el-icon name="el-icon-circle-close" @click="close" />
<a href="https://www.aliyun.com/minisite/goods?userCode=kqyyppx2">
<img src="~@/assets/image/aliyun-abs.jpg" width="300" />
</a>

View File

@@ -4,7 +4,7 @@
<el-header class="header" height="50px">
<p>选择头像</p>
<p class="tools">
<i class="el-icon-close" @click="$emit('close', 0)" />
<legacy-el-icon name="el-icon-close" @click="$emit('close', 0)" />
</p>
</el-header>
<el-main class="main">
@@ -45,28 +45,28 @@
<el-button
size="small"
plain
icon="el-icon-upload"
:icon="$legacyIcon('el-icon-upload')"
@click="clickUpload"
>上传图片
</el-button>
<el-button
size="small"
plain
icon="el-icon-refresh"
:icon="$legacyIcon('el-icon-refresh')"
@click="refreshCrop"
>刷新
</el-button>
<el-button
size="small"
plain
icon="el-icon-refresh-left"
:icon="$legacyIcon('el-icon-refresh-left')"
@click="rotateLeft"
>左转
</el-button>
<el-button
size="small"
plain
icon="el-icon-refresh-right"
:icon="$legacyIcon('el-icon-refresh-right')"
@click="rotateRight"
>右转
</el-button>

View File

@@ -3,7 +3,7 @@
<div class="reward" v-show="isShow">
<div class="title">
<span>Donate</span>
<i class="el-icon-circle-close" @click="close" />
<legacy-el-icon name="el-icon-circle-close" @click="close" />
</div>
<div class="main">
<div class="pay-box">

View File

@@ -2,7 +2,7 @@
<!-- 新消息提示组件 -->
<div class="notify-box">
<div class="lbox">
<el-avatar size="medium" :src="avatar" />
<el-avatar size="default" :src="avatar" />
</div>
<div class="rbox">
<div class="xheader">

View File

@@ -2,14 +2,14 @@
<!-- 新消息提示组件 -->
<div class="notify-box pointer">
<div class="lbox">
<el-avatar size="medium" shape="square" :src="avatar" />
<el-avatar size="default" shape="square" :src="avatar" />
</div>
<div class="rbox">
<div class="xheader">
<p class="title">
{{ talk_type == 1 ? '私信消息通知' : '群聊消息通知' }}
</p>
<p class="time"><i class="el-icon-time" /> {{ formatTime(datetime) }}</p>
<p class="time"><legacy-el-icon name="el-icon-time" /> {{ formatTime(datetime) }}</p>
</div>
<div class="xbody">
<p>@{{ nickname }}</p>

View File

@@ -5,7 +5,7 @@
<el-header class="header" height="50px">
<p>添加好友</p>
<p class="tools">
<i class="el-icon-close" @click="close" />
<legacy-el-icon name="el-icon-close" @click="close" />
</p>
</el-header>
<el-main class="main">
@@ -13,7 +13,7 @@
v-model="mobile"
id="serach-mobile"
class="input"
prefix-icon="el-icon-search"
:prefix-icon="$legacyIcon('el-icon-search')"
placeholder="请输入对方手机号(精确查找)"
clearable
@keyup.enter="onSubmit"

View File

@@ -2,7 +2,7 @@
<div class="lum-dialog-mask animated fadeIn">
<el-container class="container" v-outside="close">
<el-header class="no-padding header" height="180px">
<i class="close el-icon-error pointer" @click="close" />
<legacy-el-icon name="el-icon-error" class="close pointer" @click="close" />
<div class="img-banner">
<img :src="detail.bag" class="img-banner" />
</div>
@@ -55,9 +55,9 @@
@keyup.enter="editRemarkSubmit"
/>
</span>
<i
<legacy-el-icon
v-show="!editRemark.isShow"
class="el-icon-edit-outline"
name="el-icon-edit-outline"
@click="clickEditRemark"
/>
</div>
@@ -76,7 +76,7 @@
v-if="detail.friend_status == 1 && detail.friend_apply == 0"
type="primary"
size="small"
icon="el-icon-circle-plus-outline"
:icon="$legacyIcon('el-icon-circle-plus-outline')"
@click="apply.isShow = true"
>添加好友
</el-button>
@@ -90,7 +90,7 @@
v-else-if="detail.friend_status == 2"
type="primary"
size="small"
icon="el-icon-s-promotion"
:icon="$legacyIcon('el-icon-s-promotion')"
@click="sendMessage(detail)"
>发消息
</el-button>
@@ -125,6 +125,7 @@
// import { ServeSearchUser } from "@/api/user";
import { ServeCreateContact, ServeEditContactRemark } from "@/api/contacts";
import { toTalk } from "@/utils/talk";
import { defaultUserBanner } from "@/assets/images";
export default {
name: "UserCardDetail",
@@ -144,7 +145,7 @@ export default {
friend_status: 0,
friend_apply: 0,
nickname_remark: "",
bag: require("@/assets/image/default-user-banner.png"),
bag: defaultUserBanner,
gender: 0,
},
@@ -425,7 +426,7 @@ export default {
padding-right: 5px;
}
.el-icon-edit-outline {
.legacy-el-icon {
margin-left: 3px !important;
}
}

View File

@@ -5,6 +5,8 @@ import store from "@/store";
import { setupElementPlus } from "@/plugins/element";
import { setupLegacyMessage } from "@/utils/message";
import { registerDirectives } from "@/core/directives";
import LegacyElIcon from "@/components/global/LegacyElIcon.vue";
import { resolveLegacyIcon } from "@/core/legacy-icon-map";
export default {
install(app) {
@@ -30,6 +32,8 @@ export default {
setupElementPlus(cardApp);
setupLegacyMessage(cardApp);
registerDirectives(cardApp);
cardApp.component("legacy-el-icon", LegacyElIcon);
cardApp.config.globalProperties.$legacyIcon = resolveLegacyIcon;
cardApp.use(router);
cardApp.use(store);
cardApp.mount(container);

View File

@@ -16,6 +16,8 @@ import {
LoginMessage,
} from "@/components/chat/messaege";
import UserCard from "@/components/user/user-card/index";
import LegacyElIcon from "@/components/global/LegacyElIcon.vue";
import { resolveLegacyIcon } from "@/core/legacy-icon-map";
const messageComponents = [
AudioMessage,
@@ -39,5 +41,7 @@ export function registerGlobalComponents(app) {
messageComponents.forEach((component) => {
app.component(component.name, component);
});
app.component("legacy-el-icon", LegacyElIcon);
app.config.globalProperties.$legacyIcon = resolveLegacyIcon;
app.use(UserCard);
}

View File

@@ -0,0 +1,96 @@
import {
ArrowDown,
ArrowLeft,
ArrowRight,
Bell,
Bottom,
ChatDotRound,
ChatLineRound,
CircleCheck,
CircleClose,
CircleCloseFilled,
CirclePlus,
Clock,
Close,
Connection,
DataLine,
Delete,
DocumentCopy,
Edit,
Flag,
Folder,
FolderOpened,
Headset,
Loading,
Microphone,
MuteNotification,
Picture,
PictureRounded,
Plus,
Promotion,
Refresh,
RefreshLeft,
RefreshRight,
Remove,
Search,
Select,
Service,
Setting,
Top,
Upload,
User,
VideoPause,
VideoPlay,
} from "@element-plus/icons-vue";
/** Element UI `el-icon-*` class names → Element Plus icon components */
export const LEGACY_ICON_MAP = {
"el-icon-arrow-down": ArrowDown,
"el-icon-bell": Bell,
"el-icon-bottom": Bottom,
"el-icon-caret-left": ArrowLeft,
"el-icon-caret-right": ArrowRight,
"el-icon-chat-dot-round": ChatDotRound,
"el-icon-chat-line-round": ChatLineRound,
"el-icon-circle-close": CircleClose,
"el-icon-circle-plus-outline": CirclePlus,
"el-icon-close": Close,
"el-icon-close-notification": MuteNotification,
"el-icon-connection": Connection,
"el-icon-delete": Delete,
"el-icon-document-copy": DocumentCopy,
"el-icon-edit-outline": Edit,
"el-icon-error": CircleCloseFilled,
"el-icon-finished": Select,
"el-icon-folder": Folder,
"el-icon-folder-opened": FolderOpened,
"el-icon-headset": Headset,
"el-icon-loading": Loading,
"el-icon-microphone": Microphone,
"el-icon-picture": Picture,
"el-icon-picture-outline-round": PictureRounded,
"el-icon-plus": Plus,
"el-icon-position": Promotion,
"el-icon-refresh": Refresh,
"el-icon-refresh-left": RefreshLeft,
"el-icon-refresh-right": RefreshRight,
"el-icon-remove-outline": Remove,
"el-icon-s-data": DataLine,
"el-icon-s-flag": Flag,
"el-icon-s-promotion": Promotion,
"el-icon-search": Search,
"el-icon-service": Service,
"el-icon-setting": Setting,
"el-icon-success": CircleCheck,
"el-icon-time": Clock,
"el-icon-top": Top,
"el-icon-upload": Upload,
"el-icon-user": User,
"el-icon-video-pause": VideoPause,
"el-icon-video-play": VideoPlay,
};
export function resolveLegacyIcon(name) {
if (!name) return null;
return LEGACY_ICON_MAP[name] || null;
}

View File

@@ -48,7 +48,7 @@ app.config.globalProperties.linkToGoods = function (goodsId, skuId) {
);
};
app.config.globalProperties.linkToStore = function (storeId) {
window.open(`${config.PC_URL}/Merchant?id=${storeId}`, "_blank");
window.open(`${config.PC_URL}merchant?id=${storeId}`, "_blank");
};
app.config.globalProperties.linkToOrders = function (sn) {
if (localStorage.getItem("storeFlag") == "false") {

View File

@@ -20,7 +20,7 @@ export default {
//标识没有值,获取用户信息
if(this.$route.query.id){
ServeGetUserSetting().then(async ({ code, result }) => {
return ServeGetUserSetting().then(async ({ code, result }) => {
// 如果result有值说明用户创建成功
if (result) {
store.commit("UPDATE_USER_INFO", {
@@ -28,15 +28,17 @@ export default {
face: result.face,
name: result.nickName,
});
console.log(result.nickName)
/**
* 用户像商家进行聊天,商家进行刷新好友列表
*/
// 判断如果是有id说明是 用户像商家进行聊天。
if (this.$route.query.id) {
await this.createTalk(this.$route.query.id);
if (typeof this.createTalk === "function") {
await this.createTalk(this.$route.query.id);
} else if (typeof this.loadChatList === "function") {
await this.loadChatList();
}
}
if(this.$route.query.goodsId && this.$route.query.skuId){
if(this.$route.query.goodsId && this.$route.query.skuId && this.goodsParams){
this.goodsParams.goodsId = this.$route.query.goodsId
this.goodsParams.skuId = this.$route.query.skuId
}
@@ -48,7 +50,7 @@ export default {
});
}else{
//标识有值代表是店铺
ServeGetStoreSetting().then(async ({ code, result }) => {
return ServeGetStoreSetting().then(async ({ code, result }) => {
if (result) {
store.commit("UPDATE_USER_INFO", {
id: result.id,
@@ -56,7 +58,9 @@ export default {
name: result.storeName,
});
//获取店铺聊天列表
await this.loadStoreChatList()
if (typeof this.loadStoreChatList === "function") {
await this.loadStoreChatList()
}
}else if (code === 200 && !result) {
setTimeout(() => {
this.loadUserSetting();
@@ -91,8 +95,8 @@ export default {
//获取商家聊天记录
loadStoreChatList() {
this.loadStatus = this.talkNum == 0 ? 0 : 1;
ServeGetStoreTalkList().then(({ code, result }) => {
if (code !== 200) return false;
return ServeGetStoreTalkList().then(({ code, result }) => {
if (code !== 200 || !Array.isArray(result)) return false;
this.$store.commit("SET_UNREAD_NUM", 0);
this.$store.commit("SET_TALK_ITEMS", {
items: result.map((item) => formatTalkItem(item)),
@@ -101,7 +105,6 @@ export default {
// 判断
if (this.$route.query.id) {
let takeData, takeIndex;
console.log(result)
result.forEach((take, index) => {
if (take.id == this.$route.query.id) {
takeData = take;

View File

@@ -1,4 +1,5 @@
import { createApp, h, onMounted, onUnmounted } from "vue";
import LegacyElIcon from "@/components/global/LegacyElIcon.vue";
let activeMenu = null;
@@ -99,7 +100,7 @@ function renderMenuItem(item, close) {
},
},
[
item.icon ? h("i", { class: item.icon }) : null,
item.icon ? h(LegacyElIcon, { name: item.icon }) : null,
h("span", item.label),
]
);

View File

@@ -1,4 +1,5 @@
import { setUserInfo, getUserInfo, getToken } from "@/utils/auth";
import { defaultAvatar, defaultUserBanner } from "@/assets/images";
// import { ServeLogout } from "@/api/user";
@@ -8,9 +9,9 @@ let state = {
// 用户昵称
name: "",
// 个性头像
face: require("@/assets/image/detault-avatar.jpg"),
face: defaultAvatar,
// 名片背景
visitCardBag: require("@/assets/image/default-user-banner.png"),
visitCardBag: defaultUserBanner,
// 当前登录状态
loginStatus: false,
toUser:""

View File

@@ -1,4 +1,4 @@
const defaultAvatar = require('@/assets/image/detault-avatar.jpg')
import { defaultAvatar } from "@/assets/images";
// 根级别的 state
const state = {

View File

@@ -10,6 +10,9 @@ const USER_SETTING = 'LILI-SETTING'
* @param {String} token
*/
export function setToken (token) {
if (token == null || token === '' || token === 'undefined' || token === 'null') {
return
}
return localStorage.setItem(
USER_TOKEN,
token

19
im/src/utils/im-mode.js Normal file
View File

@@ -0,0 +1,19 @@
/**
* IM 身份判断(与 main-mixin.loadUserSetting 一致)
* - 买家从商城「联系客服」进入URL 带 ?id=店铺ID
* - 商家从商家后台进入URL 无 id
*/
export function isBuyerImMode(route) {
return !!route?.query?.id;
}
export function isStoreImMode(route) {
return !isBuyerImMode(route);
}
export function syncImTokenFromRoute(route, setTokenFn) {
const token = route?.query?.token;
if (token) {
setTokenFn(token);
}
}

View File

@@ -28,6 +28,9 @@ const errorHandler = (error) => {
removeAll();
location.reload();
} else if (error.response.status == 403) {
if (error.config?.skipAuthDialog) {
return Promise.reject(error);
}
if(!isRefreshing){
/**
@@ -80,8 +83,8 @@ request.interceptors.request.use((config) => {
if (token) {
config.headers["accessToken"] = `${token}`;
return config;
}
return config;
}, errorHandler);
// 响应拦截器

View File

@@ -46,6 +46,8 @@ export function formatTalkItem (params) {
};
Object.assign(options, params);
options.talk_type = options.talk_type || 1;
options.receiver_id = options.receiver_id || options.userId;
options.index_name = `${options.talk_type}_${options.receiver_id}`;
return options;

View File

@@ -18,7 +18,7 @@
<el-tooltip content="我的消息" placement="right" :visible-arrow="false">
<router-link to="/message">
<div class="menu-items" :class="{ active: idx == 0 }">
<i class="el-icon-chat-line-round" />
<legacy-el-icon name="el-icon-chat-line-round" />
<span v-show="unreadNum" class="notify"></span>
</div>
</router-link>

View File

@@ -20,7 +20,7 @@
</el-header>
<el-header height="60px" class="header">
<div class="from-search">
<el-input v-model="input" prefix-icon="el-icon-search" placeholder="搜索好友" size="small" />
<el-input v-model="input" :prefix-icon="$legacyIcon('el-icon-search')" placeholder="搜索好友" size="small" />
</div>
</el-header>
<!-- <el-header height="118px" class="logo-header">
@@ -48,10 +48,10 @@
</el-header>
<!-- 对话列表栏 -->
<el-scrollbar tag="section" ref="menusScrollbar" class="full-height" :native="false">
<el-scrollbar tag="section" ref="menusScrollbar" class="full-height" :native="false" @scroll="onMenusScroll">
<el-main class="main">
<p v-show="loadStatus === 0" class="empty-data">
<i class="el-icon-loading" /> 数据加载中...
<p v-show="loadStatus === 0 && userTalkItem.length === 0" class="empty-data">
<legacy-el-icon name="el-icon-loading" /> 数据加载中...
</p>
<p v-show="loadStatus === 1 && talkNum === 0" class="empty-data">
@@ -61,7 +61,7 @@
<p v-show="loadStatus === 1 && talkNum > 0" class="main-menu">
<span class="title">对话记录({{ talkNum }})</span>
<!-- <el-tag size="mini" type="danger" v-if="myUnreadNum"
<!-- <el-tag size="small" type="danger" v-if="myUnreadNum"
>未读{{ myUnreadNum }}</el-tag
> -->
</p>
@@ -78,7 +78,7 @@
<face :text="item.face" v-if="item.face"></face>
<face-null :text="item.name" v-else></face-null>
<div v-show="item.is_top == 0" class="top-mask" @click.stop="topChatItem(item)">
<i class="el-icon-top" />
<legacy-el-icon name="el-icon-top" />
</div>
</div>
<div class="card-box">
@@ -168,6 +168,7 @@ import { ServeDeleteContact, ServeEditContactRemark } from "@/api/contacts";
import { beautifyTime } from "@/utils/functions";
import { formatTalkItem, findTalkIndex, getCacheIndexName } from "@/utils/talk";
import { setToken } from "@/utils/auth";
import { syncImTokenFromRoute } from "@/utils/im-mode";
const title = document.title;
@@ -286,6 +287,7 @@ export default {
},
beforeRouteUpdate (to, from, next) {
syncImTokenFromRoute(to, setToken);
let index_name = getCacheIndexName();
if (index_name) this.clickTab(index_name);
// 更新商品参数
@@ -293,7 +295,7 @@ export default {
next();
},
beforeCreate () {
setToken(this.$route.query.token);
syncImTokenFromRoute(this.$route, setToken);
},
async created () {
await this.initialize();
@@ -308,9 +310,6 @@ export default {
},
mounted () {
this.scrollEvent();
},
beforeUnmount () {
document.title = title;
clearInterval(this.interval);
@@ -368,40 +367,34 @@ export default {
}
},
// 监听自定义滚动条事件
scrollEvent () {
let scrollbarEl = this.$refs.menusScrollbar.wrap;
scrollbarEl.onscroll = () => {
this.subHeaderShadow = scrollbarEl.scrollTop > 0;
};
// 左侧会话列表滚动时,控制置顶栏阴影
onMenusScroll ({ scrollTop }) {
this.subHeaderShadow = scrollTop > 0;
},
// 获取用户对话列表
loadChatList () {
this.loadStatus = this.talkNum == 0 ? 0 : 1;
ServeGetTalkList()
return ServeGetTalkList()
.then(({ code, result }) => {
if (code !== 200) return false;
if (code !== 200 || !Array.isArray(result)) return false;
this.$store.commit("SET_UNREAD_NUM", 0);
this.$store.commit("SET_TALK_ITEMS", {
items: result.map((item) => formatTalkItem(item)),
});
// 判断
if (this.$route.query.id) {
let takeData, takeIndex;
console.log("用户result", result)
this.talkItems.forEach((take, index) => {
if (take.userId == this.$route.query.id) {
takeData = take;
takeIndex = index;
}
});
this.$nextTick(() =>
this.clickTab(this.$route.query.id, takeData, takeIndex)
const targetId = this.$route.query.id;
const takeIndex = this.talkItems.findIndex(
(take) => take.userId == targetId
);
const takeData = takeIndex >= 0 ? this.talkItems[takeIndex] : null;
if (takeData) {
this.clickTab(targetId, takeData, takeIndex);
}
}
})
.catch(() => {})
.finally(() => {
this.loadStatus = 1;
});
@@ -414,18 +407,21 @@ export default {
let item =
this.talks.find((item) => {
return item.userId == id;
}) || {};
}) || val || {};
// 点击当前栏目存储当前用户的信息
this.$store.state.user.toUser = val;
this.$store.state.user.toUser = val || item;
let nickname = item.name;
const talkId = item.id || val?.id;
if (!talkId) return;
this.params = {
talk_type: 1,
receiver_id: item.userId,
receiver_id: item.userId || id,
nickname,
is_robot: item.is_robot,
talkId: item.id, //聊天对话的id
talkId,
clickFlag: true
};