升级Vue3,iView替换ElementPlus

- 删除babel配置、更新依赖与入口初始化
- 全量替换UI组件、样式适配,新增迁移文档与标签/过滤器自动化替换脚本
This commit is contained in:
lifenlong
2026-06-05 17:49:43 +08:00
parent 615ee91511
commit 832fda813b
322 changed files with 25693 additions and 24453 deletions

View File

@@ -43,14 +43,9 @@
</div>
</template>
<script>
import PrismEditor from "vue-prism-editor";
import "vue-prism-editor/dist/VuePrismEditor.css";
import { PrismEditor } from "vue-prism-editor";
import "vue-prism-editor/dist/prismeditor.min.css";
import "prismjs/themes/prism-okaidia.css";
import Vue from "vue";
import { Select, Option } from "element-ui";
Vue.use(Select);
Vue.use(Option);
export default {
name: "TalkCodeBlock",
components: {
@@ -284,29 +279,29 @@ export default {
max-width: 100%;
}
/deep/ .el-input__inner {
:deep(.el-input__inner ) {
border-radius: 0;
width: 130px;
}
/deep/ pre {
:deep(pre ) {
border-radius: 0;
}
/deep/ .prism-editor-wrapper pre::-webkit-scrollbar {
:deep(.prism-editor-wrapper pre::-webkit-scrollbar ) {
background-color: #272822;
}
/deep/ .prism-editor-wrapper pre::-webkit-scrollbar-thumb {
:deep(.prism-editor-wrapper pre::-webkit-scrollbar-thumb ) {
background-color: #41413f;
cursor: pointer;
}
/deep/ .prism-editor-wrapper::-webkit-scrollbar {
:deep(.prism-editor-wrapper::-webkit-scrollbar ) {
background-color: #272822;
}
/deep/ .prism-editor-wrapper::-webkit-scrollbar-thumb {
:deep(.prism-editor-wrapper::-webkit-scrollbar-thumb ) {
background-color: rgb(114, 112, 112);
cursor: pointer;
}

View File

@@ -131,7 +131,7 @@ export default {
height: 600px;
}
/deep/.el-scrollbar__wrap {
:deep(.el-scrollbar__wrap ) {
overflow-x: hidden;
}

View File

@@ -397,7 +397,7 @@ export default {
};
</script>
<style lang="less" scoped>
/deep/.el-scrollbar__wrap {
:deep(.el-scrollbar__wrap ) {
overflow-x: hidden;
}

View File

@@ -125,7 +125,7 @@ export default {
justify-content: center;
box-sizing: border-box;
/deep/.el-divider {
:deep(.el-divider ) {
background: white;
}

View File

@@ -7,8 +7,12 @@
:style="getImgStyle(src)"
:preview-src-list="[src]"
>
<div slot="error" class="image-slot">图片加载失败...</div>
<div slot="placeholder" class="image-slot">图片加载...</div>
<template #error>
<div class="image-slot">图片加载失败...</div>
</template>
<template #placeholder>
<div class="image-slot">图片加载中...</div>
</template>
</el-image>
</div>
</template>
@@ -31,7 +35,7 @@ export default {
</script>
<style lang="less" scoped>
.image-message {
/deep/.el-image {
:deep(.el-image) {
border-radius: 5px;
cursor: pointer;
background: #f1efef;

View File

@@ -17,7 +17,6 @@
</template>
<script>
import { Tabs, TabPane } from 'element-ui'
import { ServeGetStoreDetail, ServeGetUserDetail, 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";
@@ -26,8 +25,6 @@ import SocketInstance from "@/im-server/socket-instance";
import { mapState, mapGetters } from "vuex";
export default {
components: {
"el-tabs": Tabs,
"el-tab-pane": TabPane,
StoreDetail,
FootPrint,
GoodsLink
@@ -271,27 +268,27 @@ export default {
</script>
<style scoped lang="less">
// /deep/ .el-tabs__nav.is-top {
// :deep(.el-tabs__nav.is-top ) {
// }
/deep/ .el-tabs__nav {
:deep(.el-tabs__nav ) {
height: 60px;
line-height: 60px;
}
/deep/ .el-tab-pane {
:deep(.el-tab-pane ) {
// margin-left: 12px;
}
/deep/.el-tabs__nav-scroll{
:deep(.el-tabs__nav-scroll) {
min-width: 362px;
}
/deep/ .el-tabs__item{
:deep(.el-tabs__item) {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
/deep/ .el-tabs__header{
:deep(.el-tabs__header) {
margin-bottom: 0;
}
</style>

View File

@@ -89,7 +89,7 @@
</el-tooltip>
</div>
<div class="price">
<span>{{ item.text.price | unitPrice('¥') }}</span>
<span>{{ $filters.unitPrice(item.text.price, '¥') }}</span>
</div>
</div>
</div>
@@ -111,14 +111,14 @@
<div>
<span class="orderGoodsName">{{ order.name }}</span>
<div class="goods-item-price">
<span>{{ order.goodsPrice | unitPrice('¥') }}</span>
<span>{{ $filters.unitPrice(order.goodsPrice, '¥') }}</span>
</div>
</div>
</div>
<div class="shared-goods">
<div class="orderGoodsTime">{{ item.text.paymentTime }}</div>
<span class="orderFlowPrice">
订单金额<span>{{ item.text.flowPrice | unitPrice('¥') }}</span>
订单金额<span>{{ $filters.unitPrice(item.text.flowPrice, '¥') }}</span>
</span>
<div class="order-status">
<el-tag
@@ -205,7 +205,6 @@ import PanelToolbar from "./PanelToolbar";
import SocketInstance from "@/im-server/socket-instance";
import { SvgMentionDown } from "@/core/icons";
import { formatTime, parseTime, copyTextToClipboard } from "@/utils/functions";
import { unitPrice } from '@/plugins/filters';
import {
ServeTalkRecords,

View File

@@ -14,7 +14,7 @@
<a class="goods_name">{{ item.goodsName }}</a>
<div style="display: flex;">
<div style="margin-top: 20px;">
<span class='goods-price'>{{ item.price | unitPrice("¥") }}</span>
<span class='goods-price'>{{ $filters.unitPrice(item.price, "¥") }}</span>
<div class="goods_store_button">
</div>
@@ -42,7 +42,7 @@
<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">{{order.goodsPrice | unitPrice("¥")}}</span>
<span class="price">{{ $filters.unitPrice(order.goodsPrice, "¥") }}</span>
</div>
<!-- <img :src="item.groupImages" alt=""> -->
@@ -77,14 +77,9 @@
</template>
<script>
import { Tag, button, Tabs, TabPane, InfiniteScroll } from 'element-ui'
import { mapState, mapGetters } from "vuex";
import { unitPrice } from '@/plugins/filters';
export default {
directives: {
"infinite-scroll": InfiniteScroll,
},
data () {
return {
noMoreList:{
@@ -310,7 +305,7 @@ export default {
font-size:12px;
}
/deep/ .el-tabs__header {
:deep(.el-tabs__header ) {
position: absolute;
width: 362px;
height: 50px;
@@ -318,7 +313,7 @@ export default {
z-index: 2;
background: #ffffff;
}
/deep/ .el-tabs__nav.is-top {
:deep(.el-tabs__nav.is-top ) {
height: 50px;
}
@@ -328,10 +323,10 @@ export default {
align-items: center;
}
/deep/.el-tabs__content {
:deep(.el-tabs__content ) {
margin-top: 50px;
}
/deep/ .el-tabs{
:deep(.el-tabs) {
width: 100%;
}
@@ -390,7 +385,7 @@ export default {
// height: calc(100vh - 110px);
// overflow-y: auto;
// }
// /deep/.el-tab-pane {
// :deep(.el-tab-pane ) {
// height: calc(100vh - 110px);
// overflow-y: auto;
// }

View File

@@ -8,7 +8,7 @@
<div style="margin-left: 13px">
<a @click="linkToGoods(goodsDetail.goodsId, goodsDetail.id)"> {{ goodsDetail.goodsName }} </a>
<div>
<span style="color: red;">{{ goodsDetail.price | unitPrice('¥') }}</span>
<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"
@@ -20,7 +20,6 @@
</template>
<script>
import { Tag, button } from 'element-ui'
import { mapState, mapGetters } from "vuex";
import SocketInstance from "@/im-server/socket-instance";
export default {
@@ -42,11 +41,6 @@ export default {
console.log(this.goodsDetail)
this.btnHide = localStorage.getItem('btnHide')
},
components: {
"el-tag": Tag,
"el-button": button,
Storage
},
methods: {
toGoods () {
alert("toGoods")

View File

@@ -35,7 +35,6 @@
</template>
<script>
import { Tag, button, rate } from 'element-ui'
export default {
data () {
return {
@@ -43,11 +42,6 @@ export default {
colors: ['#99A9BF', '#F7BA2A', '#FF9900']
}
},
components: {
"el-tag": Tag,
"el-button": button,
"el-rate": rate
},
methods: {
},
props: {

View File

@@ -3,10 +3,16 @@
<el-container class="editor-container">
<el-header class="no-padding toolbar" height="35px">
<ul>
<li v-popover:popoverEmoticon>
<i class="iconfont icon-icon_im_face" style="font-size: 15px" />
<p class="tip-title">表情符号</p>
</li>
<el-popover ref="popoverEmoticon" placement="top-start" trigger="click" width="300"
popper-class="no-padding el-popover-em">
<template #reference>
<li>
<i class="iconfont icon-icon_im_face" style="font-size: 15px" />
<p class="tip-title">表情符号</p>
</li>
</template>
<MeEditorEmoticon ref="editorEmoticon" @selected="selecteEmoticon" />
</el-popover>
<!-- <li @click="codeBlock.isShow = true">
<i class="iconfont icon-daima" />
<p class="tip-title">代码片段</p>
@@ -59,11 +65,6 @@
<!-- </p>-->
</ul>
<el-popover ref="popoverEmoticon" placement="top-start" trigger="click" width="300"
popper-class="no-padding el-popover-em">
<MeEditorEmoticon ref="editorEmoticon" @selected="selecteEmoticon" />
</el-popover>
<form enctype="multipart/form-data" style="display: none" ref="fileFrom">
<input type="file" ref="restFile" accept="image/*" @change="uploadImageChange" />
<input type="file" ref="restFile2" @change="uploadFileChange" />
@@ -76,7 +77,7 @@
</el-container>
<!-- 图片查看器 -->
<MeEditorImageView ref="imageViewer" v-model="imageViewer.isShow" :file="imageViewer.file"
<MeEditorImageView ref="imageViewer" v-model:show="imageViewer.isShow" :file="imageViewer.file"
@confirm="confirmUploadImage" />
<MeEditorRecorder v-if="recorder" @close="recorder = false" />
@@ -86,7 +87,7 @@
@confirm="confirmCodeBlock" />
<!-- 文件上传管理器 -->
<MeEditorFileManage ref="filesManager" v-model="filesManager.isShow" />
<MeEditorFileManage ref="filesManager" v-model:show="filesManager.isShow" />
<MeEditorVote v-if="vote.isShow" @close="
() => {
@@ -335,7 +336,7 @@ export default {
});
}
this.$refs.popoverEmoticon.doClose();
this.$refs.popoverEmoticon.hide();
},
},
};

View File

@@ -332,7 +332,7 @@ export default {
}
}
/deep/ .el-image {
:deep(.el-image ) {
width: 100%;
height: 100%;
transition: ease-in 0.3s;

View File

@@ -78,21 +78,14 @@
</template>
<script>
import Vue from 'vue'
import { SvgNotData } from '@/core/icons'
import { Progress } from 'element-ui'
Vue.use(Progress)
import { ServeFindFileSplitInfo, ServeFileSubareaUpload } from '@/api/upload'
import { formatSize, getFileExt, parseTime } from '@/utils/functions'
import { ServeSendTalkFile } from '@/api/chat'
export default {
name: 'MeEditorFileManage',
model: {
prop: 'show',
event: 'close',
},
emits: ['close', 'update:show'],
props: {
show: Boolean,
},
@@ -141,6 +134,7 @@ export default {
},
methods: {
closeBox() {
this.$emit('update:show', false)
this.$emit('close', false)
},

View File

@@ -33,10 +33,7 @@
<script>
export default {
name: 'MeEditorImageView',
model: {
prop: 'show',
event: 'close',
},
emits: ['close', 'update:show', 'confirm'],
props: {
show: Boolean,
file: File,
@@ -60,6 +57,7 @@ export default {
return false
}
this.$emit('update:show', false)
this.$emit('close', false)
},
loadFile(file) {

View File

@@ -188,7 +188,7 @@ export default {
return `${hour}:${minute}:${seconds}`
},
},
destroyed() {
beforeUnmount() {
if (this.recorder) {
this.destroyRecorder()
}

View File

@@ -37,11 +37,12 @@
placeholder="请输入选项内容"
:maxlength="120"
>
<span
slot="prefix"
style="margin-left:7px;"
v-text="String.fromCharCode(65 + index)"
/>
<template #prefix>
<span
style="margin-left:7px;"
v-text="String.fromCharCode(65 + index)"
/>
</template>
</el-input>
</div>
<div class="rbox">
@@ -154,7 +155,7 @@ export default {
return false
}
this.$delete(this.options, index)
this.options.splice(index, 1)
},
},
}
@@ -184,7 +185,7 @@ export default {
.lbox {
width: 100%;
/deep/.el-input__prefix {
:deep(.el-input__prefix ) {
height: 36px;
line-height: 36px;
}
@@ -220,7 +221,7 @@ export default {
}
}
/deep/.el-radio__input.is-checked + .el-radio__label {
:deep(.el-radio__input.is-checked + .el-radio__label ) {
color: #606266;
}
</style>

View File

@@ -9,7 +9,7 @@
<p class="title">
{{ talk_type == 1 ? '私信消息通知' : '群聊消息通知' }}
</p>
<p class="time"><i class="el-icon-time" /> {{ datetime | format }}</p>
<p class="time"><i class="el-icon-time" /> {{ formatTime(datetime) }}</p>
</div>
<div class="xbody">
<p>@{{ nickname }}</p>
@@ -50,8 +50,8 @@ export default {
data() {
return {}
},
filters: {
format(datetime) {
methods: {
formatTime(datetime) {
datetime = datetime || new Date()
return parseTime(datetime, '{m}/{d} {h}:{i} 分')
},

View File

@@ -1,5 +1,5 @@
<template>
<svg :class="svgClass" aria-hidden="true" v-on="$listeners">
<svg :class="svgClass" aria-hidden="true" v-bind="$attrs">
<use :xlink:href="iconName" />
</svg>
</template>

View File

@@ -16,7 +16,7 @@
prefix-icon="el-icon-search"
placeholder="请输入对方手机号(精确查找)"
clearable
@keyup.enter.native="onSubmit"
@keyup.enter="onSubmit"
@input="error = false"
/>
<p v-show="error" class="error">

View File

@@ -31,7 +31,7 @@
<div class="card-rows no-select">
<div class="card-row">
<label>手机</label>
<span>{{ detail.mobile | mobile }}</span>
<span>{{ formatMobile(detail.mobile) }}</span>
</div>
<div class="card-row">
<label>昵称</label>
@@ -39,7 +39,7 @@
</div>
<div class="card-row">
<label>性别</label>
<span>{{ detail.gender | gender }}</span>
<span>{{ formatGender(detail.gender) }}</span>
</div>
<div v-show="detail.friend_status == 2" class="card-row">
<label>备注</label>
@@ -134,18 +134,6 @@ export default {
default: 0,
},
},
filters: {
gender(value) {
let arr = ["未知", "男", "女"];
return arr[value] || "未知";
},
// 手机号格式化
mobile(value) {
return (
value.substr(0, 3) + " " + value.substr(3, 4) + " " + value.substr(7, 4)
);
},
},
data() {
return {
detail: {
@@ -179,6 +167,16 @@ export default {
// this.loadUserDetail();
},
methods: {
formatGender(value) {
const arr = ["未知", "男", "女"];
return arr[value] || "未知";
},
formatMobile(value) {
if (!value) return "";
return (
value.substr(0, 3) + " " + value.substr(3, 4) + " " + value.substr(7, 4)
);
},
close() {
if (this.contacts) return false;

View File

@@ -1,33 +1,40 @@
import UserCardDetail from './UserCardDetail'
import { createApp, h } from "vue";
import UserCardDetail from "./UserCardDetail";
import router from "@/router";
import store from "@/store";
import { setupElementPlus } from "@/plugins/element";
import { setupLegacyMessage } from "@/utils/message";
import { registerDirectives } from "@/core/directives";
export default {
install(Vue) {
install(app) {
function user(user_id, options) {
let _vm = this
const el = new Vue({
router: _vm.$router,
store: _vm.$store,
render(h) {
return h(UserCardDetail, {
on: {
close: () => {
el.$destroy()
document.body.removeChild(el.$el)
},
changeRemark: data => {
options.editRemarkCallbak && options.editRemarkCallbak(data)
},
},
props: {
user_id,
},
})
},
}).$mount()
const container = document.createElement("div");
document.body.appendChild(container);
document.body.appendChild(el.$el)
const cardApp = createApp({
render() {
return h(UserCardDetail, {
user_id,
onClose: () => {
cardApp.unmount();
document.body.removeChild(container);
},
onChangeRemark: (data) => {
options.editRemarkCallbak && options.editRemarkCallbak(data);
},
});
},
});
setupElementPlus(cardApp);
setupLegacyMessage(cardApp);
registerDirectives(cardApp);
cardApp.use(router);
cardApp.use(store);
cardApp.mount(container);
}
Vue.prototype.$user = user
app.config.globalProperties.$user = user;
},
}
};