mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-06-22 18:10:26 +08:00
升级Vue3,iView替换ElementPlus
- 删除babel配置、更新依赖与入口初始化 - 全量替换UI组件、样式适配,新增迁移文档与标签/过滤器自动化替换脚本
This commit is contained in:
@@ -1,19 +1,10 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
],
|
||||
presets: ["@vue/cli-plugin-babel/preset"],
|
||||
plugins: [
|
||||
[
|
||||
"import",
|
||||
{
|
||||
"libraryName": "element-ui",
|
||||
"styleLibraryName": "theme-chalk"
|
||||
}
|
||||
],
|
||||
[
|
||||
"prismjs",
|
||||
{
|
||||
"languages": [
|
||||
languages: [
|
||||
"html",
|
||||
"css",
|
||||
"less",
|
||||
@@ -42,10 +33,10 @@ module.exports = {
|
||||
"yml",
|
||||
"md",
|
||||
"erlang",
|
||||
"ini"
|
||||
"ini",
|
||||
],
|
||||
"theme": "okaidia"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
theme: "okaidia",
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
@@ -8,43 +8,43 @@
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"axios": "^0.21.4",
|
||||
"babel-plugin-prismjs": "^2.0.1",
|
||||
"core-js": "^3.6.5",
|
||||
"element-ui": "^2.14.1",
|
||||
"core-js": "^3.36.0",
|
||||
"element-plus": "^2.6.3",
|
||||
"js-audio-recorder": "^1.0.6",
|
||||
"js-base64": "^2.5.1",
|
||||
"mavon-editor": "^2.10.4",
|
||||
"nprogress": "^0.2.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"qs": "^6.9.4",
|
||||
"svg-sprite-loader": "^5.0.0",
|
||||
"vue": "^2.6.11",
|
||||
"vue-contextmenujs": "^1.3.13",
|
||||
"vue-cropper": "^0.5.5",
|
||||
"vue-prism-editor": "^0.5.1",
|
||||
"vue-router": "^3.4.9",
|
||||
"vue-virtual-scroller": "^1.1.2",
|
||||
"vuex": "^3.5.1"
|
||||
"vue": "^3.4.21",
|
||||
"vue-cropper": "^1.1.1",
|
||||
"vue-prism-editor": "2.0.0-alpha.2",
|
||||
"vue-router": "^4.3.0",
|
||||
"vue-virtual-scroller": "^3.0.4",
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^5.0.8",
|
||||
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"@vue/compiler-sfc": "^3.4.21",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-plugin-import": "^1.13.1",
|
||||
"compression-webpack-plugin": "^5.0.0",
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"less": "^3.0.4",
|
||||
"less-loader": "^5.0.0",
|
||||
"eslint-plugin-vue": "^9.22.0",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^11.1.4",
|
||||
"postcss": "^8.4.20",
|
||||
"style-resources-loader": "^1.4.1",
|
||||
"vue-cli-plugin-style-resources-loader": "^0.1.4",
|
||||
"vue-svg-component-runtime": "^1.0.1",
|
||||
"vue-svg-icon-loader": "^2.1.1",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"webpack": "^5.75.0"
|
||||
"webpack": "^5.95.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
@@ -52,7 +52,7 @@
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
|
||||
21
im/scripts/fix-deep.js
Normal file
21
im/scripts/fix-deep.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
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 (/\.(vue|less)$/.test(entry.name)) files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
const root = path.join(__dirname, "../src");
|
||||
for (const file of walk(root)) {
|
||||
let content = fs.readFileSync(file, "utf8");
|
||||
const next = content.replace(/\/deep\/\s*([^\{]+)\{/g, ":deep($1) {");
|
||||
if (next !== content) {
|
||||
fs.writeFileSync(file, next);
|
||||
console.log("fixed:", file);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { setRefreshView } from "@/utils/app-bridge";
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
data () {
|
||||
@@ -35,10 +37,11 @@ export default {
|
||||
},
|
||||
|
||||
mounted () {
|
||||
setRefreshView(this.refreshView.bind(this));
|
||||
window.addEventListener('beforeunload', e => this.beforeunloadHandler(e))
|
||||
window.addEventListener('unload', e => this.unloadHandler(e))
|
||||
},
|
||||
destroyed () {
|
||||
beforeUnmount () {
|
||||
window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e))
|
||||
window.removeEventListener('unload', e => this.unloadHandler(e))
|
||||
},
|
||||
|
||||
@@ -173,7 +173,7 @@
|
||||
}
|
||||
|
||||
.main {
|
||||
/deep/.el-input__inner {
|
||||
:deep(.el-input__inner ) {
|
||||
border-radius: 1px !important;
|
||||
}
|
||||
|
||||
@@ -238,4 +238,44 @@
|
||||
|
||||
.flex-10 {
|
||||
flex: 10;
|
||||
}
|
||||
|
||||
.im-contextmenu {
|
||||
padding: 4px 0;
|
||||
background: #fff;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.im-contextmenu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background: #f5f7fa;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
color: #c0c4cc;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.im-contextmenu-divider {
|
||||
height: 1px;
|
||||
margin: 4px 0;
|
||||
background: #ebeef5;
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
flex-shrink: 0;
|
||||
height: 40px;
|
||||
|
||||
/deep/.el-input .el-input__inner {
|
||||
:deep(.el-input .el-input__inner ) {
|
||||
border-radius: 20px;
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/deep/.el-input__inner {
|
||||
:deep(.el-input__inner ) {
|
||||
border-radius: 1px !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/deep/.text-message {
|
||||
:deep(.text-message ) {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ export default {
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
/deep/.el-scrollbar__wrap {
|
||||
:deep(.el-scrollbar__wrap ) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -397,7 +397,7 @@ export default {
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/deep/.el-scrollbar__wrap {
|
||||
:deep(.el-scrollbar__wrap ) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ export default {
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
/deep/.el-divider {
|
||||
:deep(.el-divider ) {
|
||||
background: white;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
// }
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -332,7 +332,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .el-image {
|
||||
:deep(.el-image ) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: ease-in 0.3s;
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -188,7 +188,7 @@ export default {
|
||||
return `${hour}:${minute}:${seconds}`
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
beforeUnmount() {
|
||||
if (this.recorder) {
|
||||
this.destroyRecorder()
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} 分')
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,49 +1,57 @@
|
||||
import Vue from 'vue'
|
||||
import Clickoutside from 'element-ui/src/utils/clickoutside'
|
||||
function onClickOutside(el, binding) {
|
||||
const handler = (e) => {
|
||||
if (!el.contains(e.target)) {
|
||||
binding.value(e);
|
||||
}
|
||||
};
|
||||
el.__clickOutsideHandler__ = handler;
|
||||
document.addEventListener("click", handler);
|
||||
}
|
||||
|
||||
// 自定义聚焦指令
|
||||
Vue.directive('focus', {
|
||||
inserted(el) {
|
||||
el.focus()
|
||||
},
|
||||
})
|
||||
function offClickOutside(el) {
|
||||
if (el.__clickOutsideHandler__) {
|
||||
document.removeEventListener("click", el.__clickOutsideHandler__);
|
||||
delete el.__clickOutsideHandler__;
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义粘贴指令
|
||||
Vue.directive('paste', {
|
||||
bind(el, binding, vnode) {
|
||||
el.addEventListener('paste', function(event) {
|
||||
//这里直接监听元素的粘贴事件
|
||||
binding.value(event)
|
||||
})
|
||||
},
|
||||
})
|
||||
export function registerDirectives(app) {
|
||||
app.directive("focus", {
|
||||
mounted(el) {
|
||||
el.focus();
|
||||
},
|
||||
});
|
||||
|
||||
// 自定义拖拽指令
|
||||
Vue.directive('drag', {
|
||||
bind(el, binding, vnode) {
|
||||
// 因为拖拽还包括拖动时的经过事件,离开事件,和进入事件,放下事件,
|
||||
// 浏览器对于拖拽的默认事件的处理是打开拖进来的资源,
|
||||
// 所以要先对这三个事件进行默认事件的禁止
|
||||
el.addEventListener('dragenter', function(event) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
})
|
||||
el.addEventListener('dragover', function(event) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
})
|
||||
el.addEventListener('dragleave', function(event) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
})
|
||||
el.addEventListener('drop', function(event) {
|
||||
// 这里阻止默认事件,并绑定事件的对象,用来在组件上返回事件对象
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
binding.value(event)
|
||||
})
|
||||
},
|
||||
})
|
||||
app.directive("paste", {
|
||||
mounted(el, binding) {
|
||||
el.addEventListener("paste", (event) => {
|
||||
binding.value(event);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// 点击其他地方隐藏指令
|
||||
Vue.directive('outside', Clickoutside)
|
||||
app.directive("drag", {
|
||||
mounted(el, binding) {
|
||||
const stop = (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
};
|
||||
el.addEventListener("dragenter", stop);
|
||||
el.addEventListener("dragover", stop);
|
||||
el.addEventListener("dragleave", stop);
|
||||
el.addEventListener("drop", (event) => {
|
||||
stop(event);
|
||||
binding.value(event);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
app.directive("outside", {
|
||||
mounted(el, binding) {
|
||||
onClickOutside(el, binding);
|
||||
},
|
||||
unmounted(el) {
|
||||
offClickOutside(el);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Vue from "vue";
|
||||
import {
|
||||
AudioMessage,
|
||||
CodeMessage,
|
||||
@@ -16,22 +15,29 @@ import {
|
||||
VoteMessage,
|
||||
LoginMessage,
|
||||
} from "@/components/chat/messaege";
|
||||
|
||||
Vue.component(AudioMessage.name, AudioMessage);
|
||||
Vue.component(CodeMessage.name, CodeMessage);
|
||||
Vue.component(ForwardMessage.name, ForwardMessage);
|
||||
Vue.component(ImageMessage.name, ImageMessage);
|
||||
Vue.component(TextMessage.name, TextMessage);
|
||||
Vue.component(VideoMessage.name, VideoMessage);
|
||||
Vue.component(VoiceMessage.name, VoiceMessage);
|
||||
Vue.component(SystemTextMessage.name, SystemTextMessage);
|
||||
Vue.component(FileMessage.name, FileMessage);
|
||||
Vue.component(InviteMessage.name, InviteMessage);
|
||||
Vue.component(RevokeMessage.name, RevokeMessage);
|
||||
Vue.component(VisitCardMessage.name, VisitCardMessage);
|
||||
Vue.component(ReplyMessage.name, ReplyMessage);
|
||||
Vue.component(VoteMessage.name, VoteMessage);
|
||||
Vue.component(LoginMessage.name, LoginMessage);
|
||||
|
||||
import UserCard from "@/components/user/user-card/index";
|
||||
Vue.use(UserCard);
|
||||
|
||||
const messageComponents = [
|
||||
AudioMessage,
|
||||
CodeMessage,
|
||||
ForwardMessage,
|
||||
ImageMessage,
|
||||
TextMessage,
|
||||
VideoMessage,
|
||||
VoiceMessage,
|
||||
SystemTextMessage,
|
||||
FileMessage,
|
||||
InviteMessage,
|
||||
RevokeMessage,
|
||||
VisitCardMessage,
|
||||
ReplyMessage,
|
||||
VoteMessage,
|
||||
LoginMessage,
|
||||
];
|
||||
|
||||
export function registerGlobalComponents(app) {
|
||||
messageComponents.forEach((component) => {
|
||||
app.component(component.name, component);
|
||||
});
|
||||
app.use(UserCard);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
/**
|
||||
* Custom icon list
|
||||
* All icons are loaded here for easy management
|
||||
*
|
||||
* 自定义图标加载表
|
||||
* 所有图标均从这里加载,方便管理
|
||||
*/
|
||||
import SvgMentionDown from '@/icons/svg/mention-down.svg?inline' // path to your '*.svg?inline' file.
|
||||
import SvgNotFount from '@/icons/svg/not-fount.svg?inline' // path to your '*.svg?inline' file.
|
||||
import SvgNote from '@/icons/svg/note.svg?inline' // path to your '*.svg?inline' file.
|
||||
import SvgNoteBook from '@/icons/svg/note-book.svg?inline' // path to your '*.svg?inline' file.
|
||||
import SvgNotData from '@/icons/svg/not-data.svg?inline' // path to your '*.svg?inline' file.
|
||||
import SvgZhuangFa from '@/icons/svg/zhuangfa.svg?inline' // path to your '*.svg?inline' file.
|
||||
import { h } from "vue";
|
||||
|
||||
export {
|
||||
SvgMentionDown,
|
||||
SvgNotFount,
|
||||
SvgNote,
|
||||
SvgNoteBook,
|
||||
SvgNotData,
|
||||
SvgZhuangFa,
|
||||
function createSvgIcon(name) {
|
||||
return {
|
||||
name: `SvgIcon_${name}`,
|
||||
inheritAttrs: false,
|
||||
render() {
|
||||
return h("svg-icon", {
|
||||
iconClass: name,
|
||||
...this.$attrs,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const SvgMentionDown = createSvgIcon("mention-down");
|
||||
export const SvgNotFount = createSvgIcon("not-fount");
|
||||
export const SvgNote = createSvgIcon("note");
|
||||
export const SvgNoteBook = createSvgIcon("note-book");
|
||||
export const SvgNotData = createSvgIcon("not-data");
|
||||
export const SvgZhuangFa = createSvgIcon("zhuangfa");
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
|
||||
import {
|
||||
Notification,
|
||||
Popover,
|
||||
Switch,
|
||||
Dropdown,
|
||||
DropdownMenu,
|
||||
DropdownItem,
|
||||
Message,
|
||||
Container,
|
||||
Header,
|
||||
Aside,
|
||||
Main,
|
||||
Footer,
|
||||
Menu,
|
||||
Submenu,
|
||||
MenuItem,
|
||||
MenuItemGroup,
|
||||
Button,
|
||||
Image,
|
||||
Loading,
|
||||
Row,
|
||||
Col,
|
||||
MessageBox,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
Divider,
|
||||
Link,
|
||||
Tooltip,
|
||||
Autocomplete,
|
||||
Scrollbar,
|
||||
Avatar,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Progress,
|
||||
Dialog,
|
||||
Checkbox,
|
||||
Tag
|
||||
} from 'element-ui'
|
||||
|
||||
Vue.use(Popover)
|
||||
Vue.use(Switch)
|
||||
Vue.use(Dropdown)
|
||||
Vue.use(DropdownMenu)
|
||||
Vue.use(DropdownItem)
|
||||
Vue.use(Container)
|
||||
Vue.use(Header)
|
||||
Vue.use(Aside)
|
||||
Vue.use(Main)
|
||||
Vue.use(Footer)
|
||||
Vue.use(Menu)
|
||||
Vue.use(Submenu)
|
||||
Vue.use(MenuItem)
|
||||
Vue.use(MenuItemGroup)
|
||||
Vue.use(Button)
|
||||
Vue.use(Image)
|
||||
Vue.use(Row)
|
||||
Vue.use(Col)
|
||||
Vue.use(Input)
|
||||
Vue.use(Form)
|
||||
Vue.use(FormItem)
|
||||
Vue.use(Divider)
|
||||
Vue.use(Link)
|
||||
Vue.use(Tooltip)
|
||||
Vue.use(Autocomplete)
|
||||
Vue.use(Scrollbar)
|
||||
Vue.use(Avatar)
|
||||
Vue.use(Radio)
|
||||
Vue.use(Checkbox)
|
||||
Vue.use(RadioGroup)
|
||||
Vue.use(Progress)
|
||||
Vue.use(Dialog)
|
||||
Vue.use(Tag)
|
||||
Vue.use(Loading.directive)
|
||||
|
||||
Vue.prototype.$notify = Notification
|
||||
Vue.prototype.$message = Message
|
||||
Vue.prototype.$confirm = MessageBox.confirm
|
||||
Vue.prototype.$prompt = MessageBox.prompt
|
||||
Vue.prototype.$alert = MessageBox.alert
|
||||
|
||||
import Contextmenu from 'vue-contextmenujs'
|
||||
Vue.use(Contextmenu)
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import Vue from 'vue';
|
||||
import SvgIcon from '@/components/svg-icon'; // svg component
|
||||
import SvgIcon from "@/components/svg-icon";
|
||||
|
||||
// register globally
|
||||
Vue.component('svg-icon', SvgIcon);
|
||||
export function registerIcons(app) {
|
||||
app.component("svg-icon", SvgIcon);
|
||||
|
||||
const req = require.context('./svg', false, /\.svg$/);
|
||||
const requireAll = requireContext => requireContext.keys().map(requireContext);
|
||||
requireAll(req);
|
||||
const req = require.context("./svg", false, /\.svg$/);
|
||||
const requireAll = (requireContext) =>
|
||||
requireContext.keys().map(requireContext);
|
||||
requireAll(req);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import store from "@/store";
|
||||
import router from "@/router";
|
||||
import { Notification } from "element-ui";
|
||||
import { ElNotification } from "element-plus";
|
||||
|
||||
class Base {
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
constructor() {
|
||||
this.$notify = Notification;
|
||||
this.$notify = ElNotification;
|
||||
}
|
||||
|
||||
getStoreInstance() {
|
||||
@@ -54,7 +54,7 @@ class Base {
|
||||
* 判断用户是否打开对话页
|
||||
*/
|
||||
isTalkPage() {
|
||||
let path = router.currentRoute.path;
|
||||
let path = router.currentRoute.value.path;
|
||||
return !(path != "/message" && path != "/");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import Base from "./base";
|
||||
import Vue from "vue";
|
||||
import router from "@/router";
|
||||
import vm from "@/main";
|
||||
import { nextTick } from "vue";
|
||||
import { getAppBridge } from "@/utils/app-bridge";
|
||||
import NewMessageNotify from "@/components/notify/NewMessageNotify";
|
||||
import { ServeClearTalkUnreadNum, ServeCreateTalkList } from "@/api/chat";
|
||||
import { formatTalkItem, findTalkIndex, toTalk } from "@/utils/talk";
|
||||
@@ -46,11 +45,11 @@ class Talk extends Base {
|
||||
this.resource = resource;
|
||||
|
||||
// 判断发送者消息是否在当前用户列表中
|
||||
if (this.sender_id && !vm.$store.state.talks.items.find(item => {
|
||||
const bridge = getAppBridge();
|
||||
if (this.sender_id && !bridge.$store.state.talks.items.find(item => {
|
||||
return item.userId == this.sender_id
|
||||
})) {
|
||||
// 没有当前用户,未在当前列表 进行重新加载
|
||||
vm.loadUserSetting('update')
|
||||
bridge.loadUserSetting('update')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,15 +146,14 @@ class Talk extends Base {
|
||||
receiver_id = this.sender_id;
|
||||
}
|
||||
|
||||
const bridge = getAppBridge();
|
||||
this.$notify({
|
||||
message: vm.$createElement(NewMessageNotify, {
|
||||
props: {
|
||||
avatar,
|
||||
talk_type,
|
||||
nickname,
|
||||
content: this.getTalkText(),
|
||||
datetime: this.resource.created_at,
|
||||
},
|
||||
message: bridge.$createElement(NewMessageNotify, {
|
||||
avatar,
|
||||
talk_type,
|
||||
nickname,
|
||||
content: this.getTalkText(),
|
||||
datetime: this.resource.created_at,
|
||||
}),
|
||||
customClass: "im-notify",
|
||||
duration: 3000,
|
||||
@@ -211,7 +209,7 @@ class Talk extends Base {
|
||||
record.userId == this.getAccountId() ||
|
||||
record.fromUser == this.getAccountId()
|
||||
) {
|
||||
Vue.nextTick(() => {
|
||||
nextTick(() => {
|
||||
el.scrollTop = el.scrollHeight;
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -2,7 +2,7 @@ import store from "@/store";
|
||||
import config from "@/config/config";
|
||||
import WsSocket from "@/plugins/ws-socket";
|
||||
import { getToken } from "@/utils/auth";
|
||||
import { Notification } from "element-ui";
|
||||
import { ElNotification } from "element-plus";
|
||||
|
||||
// 引入消息处理类
|
||||
import KeyboardEvent from "@/im-server/event/keyboard";
|
||||
@@ -86,7 +86,7 @@ class SocketInstance {
|
||||
});
|
||||
|
||||
this.socket.on("event_error", (data) => {
|
||||
Notification({
|
||||
ElNotification({
|
||||
title: "友情提示",
|
||||
message: data.message,
|
||||
type: "warning",
|
||||
|
||||
137
im/src/main.js
137
im/src/main.js
@@ -1,82 +1,65 @@
|
||||
import 'core-js/stable'
|
||||
import 'regenerator-runtime/runtime'
|
||||
import "core-js/stable";
|
||||
|
||||
// 引入 Vue 和应用程序组件
|
||||
import Vue from 'vue'
|
||||
import App from '@/App'
|
||||
|
||||
// 引入 store 和 router
|
||||
import store from '@/store'
|
||||
import router from '@/router'
|
||||
|
||||
// 引入自定义的 mixin
|
||||
import MainMixin from './mixins/main-mixin'
|
||||
|
||||
// 引入全局组件
|
||||
import face from '@/components/face'
|
||||
import faceNull from '@/components/face-null'
|
||||
|
||||
// 引入配置文件和其他核心模块
|
||||
import { createApp } from "vue";
|
||||
import App from "@/App.vue";
|
||||
import store from "@/store";
|
||||
import router from "@/router";
|
||||
import MainMixin from "./mixins/main-mixin";
|
||||
import face from "@/components/face";
|
||||
import faceNull from "@/components/face-null";
|
||||
import config from "@/config/config";
|
||||
import './core/lazy-use'
|
||||
import './core/global-component'
|
||||
import './core/filter'
|
||||
import './core/directives'
|
||||
|
||||
// 引入权限控制和图标库
|
||||
import '@/permission'
|
||||
import '@/icons'
|
||||
|
||||
// 引入自定义过滤器,并注册为全局过滤器
|
||||
import { setupElementPlus } from "@/plugins/element";
|
||||
import { setupLegacyMessage } from "@/utils/message";
|
||||
import { registerDirectives } from "./core/directives";
|
||||
import { registerGlobalComponents } from "./core/global-component";
|
||||
import { registerIcons } from "@/icons";
|
||||
import * as filters from "./plugins/filters";
|
||||
Object.keys(filters).forEach((key) => {
|
||||
Vue.filter(key, filters[key]);
|
||||
|
||||
import VueVirtualScroller from "vue-virtual-scroller";
|
||||
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
|
||||
|
||||
import { setupContextmenu } from "@/plugins/contextmenu";
|
||||
import "@/permission";
|
||||
import "@/assets/css/global.less";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
setupElementPlus(app);
|
||||
setupLegacyMessage(app);
|
||||
registerDirectives(app);
|
||||
registerGlobalComponents(app);
|
||||
registerIcons(app);
|
||||
|
||||
app.use(store);
|
||||
app.use(router);
|
||||
setupContextmenu(app);
|
||||
app.use(VueVirtualScroller);
|
||||
app.mixin(MainMixin);
|
||||
|
||||
app.component("face", face);
|
||||
app.component("face-null", faceNull);
|
||||
app.component("RecycleScroller", VueVirtualScroller.RecycleScroller);
|
||||
|
||||
app.config.globalProperties.$filters = filters;
|
||||
app.config.globalProperties.linkToGoods = function (goodsId, skuId) {
|
||||
window.open(
|
||||
`${config.PC_URL}goodsDetail?skuId=${skuId}&goodsId=${goodsId}`,
|
||||
"_blank"
|
||||
);
|
||||
};
|
||||
app.config.globalProperties.linkToStore = function (storeId) {
|
||||
window.open(`${config.PC_URL}/Merchant?id=${storeId}`, "_blank");
|
||||
};
|
||||
app.config.globalProperties.linkToOrders = function (sn) {
|
||||
if (localStorage.getItem("storeFlag") == "false") {
|
||||
window.open(`${config.PC_STORE}order-detail?sn=${sn}`, "_blank");
|
||||
} else {
|
||||
window.open(`${config.PC_URL}home/OrderDetail?sn=${sn}`, "_blank");
|
||||
}
|
||||
};
|
||||
|
||||
router.isReady().then(() => {
|
||||
app.mount("#app");
|
||||
});
|
||||
|
||||
|
||||
import VueVirtualScroller from 'vue-virtual-scroller'
|
||||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||
Vue.component('RecycleScroller', VueVirtualScroller.RecycleScroller)
|
||||
|
||||
// 引入自定义全局css
|
||||
import '@/assets/css/global.less'
|
||||
|
||||
// 关闭生产提示
|
||||
Vue.config.productionTip = false
|
||||
|
||||
// 注册全局 mixin
|
||||
Vue.mixin(MainMixin)
|
||||
Vue.component('face', face)
|
||||
Vue.component('face-null', faceNull)
|
||||
|
||||
|
||||
// 添加自定义原型方法
|
||||
Vue.prototype.linkToGoods = function (goodsId, skuId) { // 跳转买家端商品
|
||||
if (localStorage.getItem('storeFlag') == 'false') {
|
||||
window.open(`${config.PC_URL}goodsDetail?skuId=${skuId}&goodsId=${goodsId}`, '_blank')
|
||||
} else {
|
||||
window.open(`${config.PC_URL}goodsDetail?skuId=${skuId}&goodsId=${goodsId}`, '_blank')
|
||||
}
|
||||
};
|
||||
Vue.prototype.linkToStore = function (storeId) { // 跳转商家端商品
|
||||
console.log(`${config.PC_URL}/Merchant?id=${storeId}`)
|
||||
window.open(`${config.PC_URL}/Merchant?id=${storeId}`, '_blank')
|
||||
};
|
||||
// 订单跳转商家订单页面
|
||||
Vue.prototype.linkToOrders = function (sn) {
|
||||
if (localStorage.getItem('storeFlag') == 'false') {
|
||||
// 商家
|
||||
window.open(`${config.PC_STORE}order-detail?sn=${sn}`, '_blank')
|
||||
} else {
|
||||
// 用户
|
||||
window.open(`${config.PC_URL}home/OrderDetail?sn=${sn}`, '_blank')
|
||||
}
|
||||
};
|
||||
|
||||
const Instance = new Vue({
|
||||
router,
|
||||
store,
|
||||
mixins: [MainMixin],
|
||||
render: h => h(App),
|
||||
}).$mount('#app')
|
||||
export default Instance
|
||||
export default app;
|
||||
|
||||
@@ -119,7 +119,7 @@ export default {
|
||||
|
||||
|
||||
reload() {
|
||||
this.$root.$children[0].refreshView();
|
||||
import("@/utils/app-bridge").then(({ refreshView }) => refreshView());
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
110
im/src/plugins/contextmenu.js
Normal file
110
im/src/plugins/contextmenu.js
Normal file
@@ -0,0 +1,110 @@
|
||||
import { createApp, h, onMounted, onUnmounted } from "vue";
|
||||
|
||||
let activeMenu = null;
|
||||
|
||||
function destroyMenu() {
|
||||
if (!activeMenu) return;
|
||||
activeMenu.app.unmount();
|
||||
if (activeMenu.el?.parentNode) {
|
||||
activeMenu.el.parentNode.removeChild(activeMenu.el);
|
||||
}
|
||||
activeMenu = null;
|
||||
}
|
||||
|
||||
function normalizeOptions(options) {
|
||||
if (Array.isArray(options)) {
|
||||
return { items: options };
|
||||
}
|
||||
return options || { items: [] };
|
||||
}
|
||||
|
||||
function showContextmenu(options) {
|
||||
destroyMenu();
|
||||
|
||||
const {
|
||||
items = [],
|
||||
event,
|
||||
x = 0,
|
||||
y = 0,
|
||||
customClass = "",
|
||||
zIndex = 3000,
|
||||
minWidth = 120,
|
||||
} = normalizeOptions(options);
|
||||
|
||||
const left = event?.clientX ?? x;
|
||||
const top = event?.clientY ?? y;
|
||||
const el = document.createElement("div");
|
||||
document.body.appendChild(el);
|
||||
|
||||
const app = createApp({
|
||||
setup() {
|
||||
const close = () => destroyMenu();
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener("click", close);
|
||||
document.addEventListener("contextmenu", close);
|
||||
document.addEventListener("wheel", close, { passive: true });
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener("click", close);
|
||||
document.removeEventListener("contextmenu", close);
|
||||
document.removeEventListener("wheel", close);
|
||||
});
|
||||
|
||||
return () =>
|
||||
h(
|
||||
"div",
|
||||
{
|
||||
class: ["im-contextmenu", customClass].filter(Boolean),
|
||||
style: {
|
||||
position: "fixed",
|
||||
left: `${left}px`,
|
||||
top: `${top}px`,
|
||||
zIndex,
|
||||
minWidth: `${minWidth}px`,
|
||||
},
|
||||
onContextmenu: (e) => e.preventDefault(),
|
||||
},
|
||||
items.flatMap((item, index) => {
|
||||
const nodes = [];
|
||||
if (item.divided && index > 0) {
|
||||
nodes.push(h("div", { class: "im-contextmenu-divider" }));
|
||||
}
|
||||
nodes.push(renderMenuItem(item, close));
|
||||
return nodes;
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
activeMenu = { app, el };
|
||||
app.mount(el);
|
||||
}
|
||||
|
||||
function renderMenuItem(item, close) {
|
||||
return h(
|
||||
"div",
|
||||
{
|
||||
class: [
|
||||
"im-contextmenu-item",
|
||||
item.customClass,
|
||||
item.disabled ? "is-disabled" : "",
|
||||
].filter(Boolean),
|
||||
onClick: (e) => {
|
||||
e.stopPropagation();
|
||||
if (item.disabled) return;
|
||||
item.onClick?.();
|
||||
close();
|
||||
},
|
||||
},
|
||||
[
|
||||
item.icon ? h("i", { class: item.icon }) : null,
|
||||
h("span", item.label),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
export function setupContextmenu(app) {
|
||||
app.config.globalProperties.$contextmenu = showContextmenu;
|
||||
}
|
||||
10
im/src/plugins/element.js
Normal file
10
im/src/plugins/element.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import ElementPlus from "element-plus";
|
||||
import zhCn from "element-plus/es/locale/lang/zh-cn";
|
||||
import "element-plus/dist/index.css";
|
||||
|
||||
export function setupElementPlus(app) {
|
||||
app.use(ElementPlus, {
|
||||
locale: zhCn,
|
||||
size: "default",
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Notification, MessageBox } from "element-ui";
|
||||
import { ElNotification, ElMessageBox } from "element-plus";
|
||||
let wsSignIn; // ws 是否是掉线状态
|
||||
import store from "@/store";
|
||||
class WsSocket {
|
||||
@@ -84,7 +84,7 @@ class WsSocket {
|
||||
store.commit('SET_WS_STATUS',true);
|
||||
wsSignIn.close();
|
||||
|
||||
Notification({
|
||||
ElNotification({
|
||||
title: "成功",
|
||||
message: "重连成功",
|
||||
type: "success",
|
||||
@@ -125,7 +125,7 @@ class WsSocket {
|
||||
* 长时间挂载页面中并且重连次数为空的时候进行提示
|
||||
*/
|
||||
if (this.config.reconnect.number == 0) {
|
||||
MessageBox("当前对话链接已失效,请从关闭重新进入。", "提示", {
|
||||
ElMessageBox("当前对话链接已失效,请从关闭重新进入。", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
closeOnPressEscape: false,
|
||||
@@ -134,7 +134,7 @@ class WsSocket {
|
||||
})
|
||||
.then(() => {
|
||||
window.close();
|
||||
Notification({
|
||||
ElNotification({
|
||||
title: "对话链接已失效提示",
|
||||
message: "请手动关闭当前页面",
|
||||
type: "error",
|
||||
@@ -142,7 +142,7 @@ class WsSocket {
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
Notification({
|
||||
ElNotification({
|
||||
title: "对话链接已失效提示",
|
||||
message: "请手动关闭当前页面",
|
||||
type: "error",
|
||||
@@ -152,7 +152,7 @@ class WsSocket {
|
||||
return false;
|
||||
}
|
||||
// 掉线重连提示
|
||||
wsSignIn = Notification({
|
||||
wsSignIn = ElNotification({
|
||||
title: "掉线重连接提示",
|
||||
message: `网络连接已断开,正在尝试重新连接......`,
|
||||
type: "error",
|
||||
|
||||
@@ -1,44 +1,38 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
const RouteView = {
|
||||
name: 'RouteView',
|
||||
render: h => h('router-view'),
|
||||
}
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: () => import('@/views/message/index'),
|
||||
meta: {
|
||||
title: '',
|
||||
needLogin: true,
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
component: () => import("@/views/message/index"),
|
||||
meta: {
|
||||
title: "",
|
||||
needLogin: true,
|
||||
},
|
||||
{
|
||||
path: '/message',
|
||||
name: 'message',
|
||||
component: () => import('@/views/message/index'),
|
||||
meta: {
|
||||
title: '消息通知',
|
||||
needLogin: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/message",
|
||||
name: "message",
|
||||
component: () => import("@/views/message/index"),
|
||||
meta: {
|
||||
title: "消息通知",
|
||||
needLogin: true,
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
name: '404 NotFound',
|
||||
component: () => import('@/views/other/404'),
|
||||
meta: {
|
||||
title: '404 NotFound',
|
||||
needLogin: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/:pathMatch(.*)*",
|
||||
name: "404 NotFound",
|
||||
component: () => import("@/views/other/404"),
|
||||
meta: {
|
||||
title: "404 NotFound",
|
||||
needLogin: false,
|
||||
},
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default new Router({
|
||||
routes,
|
||||
mode: 'history',
|
||||
})
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import { createStore } from "vuex";
|
||||
|
||||
import user from './modules/user'
|
||||
import talks from './modules/talk'
|
||||
import notify from './modules/notify'
|
||||
import settings from './modules/settings'
|
||||
import emoticon from './modules/emoticon'
|
||||
import dialogue from './modules/dialogue'
|
||||
import note from './modules/note'
|
||||
import user from "./modules/user";
|
||||
import talks from "./modules/talk";
|
||||
import notify from "./modules/notify";
|
||||
import settings from "./modules/settings";
|
||||
import emoticon from "./modules/emoticon";
|
||||
import dialogue from "./modules/dialogue";
|
||||
import note from "./modules/note";
|
||||
|
||||
import state from './state'
|
||||
import getters from './getters'
|
||||
import mutations from './mutations'
|
||||
import state from "./state";
|
||||
import getters from "./getters";
|
||||
import mutations from "./mutations";
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
const store = new Vuex.Store({
|
||||
const store = createStore({
|
||||
modules: {
|
||||
user,
|
||||
notify,
|
||||
@@ -28,6 +25,6 @@ const store = new Vuex.Store({
|
||||
state,
|
||||
getters,
|
||||
mutations,
|
||||
})
|
||||
});
|
||||
|
||||
export default store
|
||||
export default store;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ServeFindUserEmoticon, ServeUploadEmoticon } from "@/api/emoticon";
|
||||
|
||||
import { ServeCollectEmoticon } from "@/api/chat";
|
||||
|
||||
import { Notification } from "element-ui";
|
||||
import { ElNotification } from "element-plus";
|
||||
|
||||
export default {
|
||||
state: {
|
||||
@@ -36,7 +36,7 @@ export default {
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
Notification({
|
||||
ElNotification({
|
||||
title: "收藏提示",
|
||||
message: "表情包收藏成功...",
|
||||
type: "success",
|
||||
@@ -46,7 +46,7 @@ export default {
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
Notification({
|
||||
ElNotification({
|
||||
title: "收藏提示",
|
||||
message: "表情包收藏失败...",
|
||||
type: "warning",
|
||||
@@ -65,7 +65,7 @@ export default {
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
Notification({
|
||||
ElNotification({
|
||||
message: "网络异常请稍后再试...",
|
||||
type: "error",
|
||||
duration: 3000,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { getSort, getMutipSort } from "@/utils/functions";
|
||||
import { formatTalkItem } from "@/utils/talk";
|
||||
import { ServeGetTalkList } from "@/api/chat";
|
||||
import store from '@/store/index.js'
|
||||
import Vue from 'vue'
|
||||
const Talk = {
|
||||
state: {
|
||||
// 用户对话列表
|
||||
@@ -40,7 +38,7 @@ const Talk = {
|
||||
|
||||
// 设置对话列表
|
||||
SET_TALK_ITEMS (state, resource) {
|
||||
Vue.set(state, 'items', resource.items)
|
||||
state.items = resource.items;
|
||||
},
|
||||
|
||||
// 更新对话节点
|
||||
|
||||
37
im/src/utils/app-bridge.js
Normal file
37
im/src/utils/app-bridge.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { h } from "vue";
|
||||
import store from "@/store";
|
||||
import router from "@/router";
|
||||
import MainMixin from "@/mixins/main-mixin";
|
||||
|
||||
let refreshViewFn = null;
|
||||
|
||||
const mixinCtx = {
|
||||
get $store() {
|
||||
return store;
|
||||
},
|
||||
get $router() {
|
||||
return router;
|
||||
},
|
||||
get $route() {
|
||||
return router.currentRoute.value;
|
||||
},
|
||||
};
|
||||
|
||||
export function setRefreshView(fn) {
|
||||
refreshViewFn = fn;
|
||||
}
|
||||
|
||||
export function refreshView() {
|
||||
refreshViewFn?.();
|
||||
}
|
||||
|
||||
export function getAppBridge() {
|
||||
return {
|
||||
$store: store,
|
||||
$router: router,
|
||||
$route: router.currentRoute.value,
|
||||
$createElement: h,
|
||||
loadUserSetting: MainMixin.methods.loadUserSetting.bind(mixinCtx),
|
||||
refreshView,
|
||||
};
|
||||
}
|
||||
11
im/src/utils/message.js
Normal file
11
im/src/utils/message.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ElMessage, ElMessageBox, ElNotification } from "element-plus";
|
||||
|
||||
export { ElNotification, ElMessage, ElMessageBox };
|
||||
|
||||
export function setupLegacyMessage(app) {
|
||||
app.config.globalProperties.$message = ElMessage;
|
||||
app.config.globalProperties.$notify = ElNotification;
|
||||
app.config.globalProperties.$confirm = ElMessageBox.confirm;
|
||||
app.config.globalProperties.$prompt = ElMessageBox.prompt;
|
||||
app.config.globalProperties.$alert = ElMessageBox.alert;
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import axios from "axios";
|
||||
import config from "@/config/config";
|
||||
import { getToken, removeAll } from "@/utils/auth";
|
||||
|
||||
import { Notification, MessageBox } from "element-ui";
|
||||
import { ElNotification, ElMessageBox } from "element-plus";
|
||||
import qs from "qs";
|
||||
|
||||
// 创建 axios 实例
|
||||
@@ -33,7 +33,7 @@ const errorHandler = (error) => {
|
||||
/**
|
||||
* 403提示将重新从商家移动端进入当前页面
|
||||
*/
|
||||
MessageBox("当前登录已失效,请从关闭重新进入。", "提示", {
|
||||
ElMessageBox("当前登录已失效,请从关闭重新进入。", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
closeOnPressEscape: false,
|
||||
@@ -43,7 +43,7 @@ const errorHandler = (error) => {
|
||||
.then(() => {
|
||||
isRefreshing = true
|
||||
window.close();
|
||||
Notification({
|
||||
ElNotification({
|
||||
title:"登录失效提示",
|
||||
message: "请手动关闭当前页面",
|
||||
type:"error",
|
||||
@@ -53,7 +53,7 @@ const errorHandler = (error) => {
|
||||
})
|
||||
.catch(() => {
|
||||
isRefreshing = true
|
||||
Notification({
|
||||
ElNotification({
|
||||
title:"登录失效提示",
|
||||
message: "请手动关闭当前页面",
|
||||
type:"error",
|
||||
@@ -63,7 +63,7 @@ const errorHandler = (error) => {
|
||||
isRefreshing = false
|
||||
}
|
||||
} else if(error.response.status == 400){
|
||||
Notification({
|
||||
ElNotification({
|
||||
message: error.response.data.message,
|
||||
position: "top-right",
|
||||
type:"error",
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<MainLayout :idx="0" ref="mainLayout">
|
||||
<el-container slot="container" class="full-height">
|
||||
<template #container>
|
||||
<el-container class="full-height">
|
||||
<!-- 左侧侧边栏 -->
|
||||
<el-aside width="320px" class="aside-box">
|
||||
<el-container class="full-height" direction="vertical">
|
||||
<!-- 搜索栏 -->
|
||||
<el-header height="60px" class="header">
|
||||
<div class="user-login" v-popover:usercard>
|
||||
<div class="user-login">
|
||||
<div class="user-box">
|
||||
<face :text="face" :name="name" class="user-face"></face>
|
||||
</div>
|
||||
@@ -142,6 +143,7 @@
|
||||
<!-- <OtherLink :toUser="toUser" :goodsParams="goodsParams" :id="id" class="flex-4"/> -->
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
</MainLayout>
|
||||
|
||||
<!-- 用户查询组件 -->
|
||||
@@ -149,8 +151,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { RecycleScroller } from 'vue-virtual-scroller'
|
||||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||
import { mapGetters, mapState } from "vuex";
|
||||
import MainLayout from "@/views/layout/MainLayout";
|
||||
import WelcomeModule from "@/components/layout/WelcomeModule";
|
||||
@@ -179,7 +179,6 @@ export default {
|
||||
UserSearch,
|
||||
OtherLink,
|
||||
WelcomeModule,
|
||||
RecycleScroller
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -247,7 +246,7 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
talkItems (val) {
|
||||
val ? this.$set(this, "userTalkItem", val) : "";
|
||||
if (val) this.userTalkItem = val;
|
||||
},
|
||||
// 搜索用户的时候 根据当前用户表进行模糊搜索
|
||||
input (val, oldVal) {
|
||||
@@ -312,7 +311,7 @@ export default {
|
||||
mounted () {
|
||||
this.scrollEvent();
|
||||
},
|
||||
destroyed () {
|
||||
beforeUnmount () {
|
||||
document.title = title;
|
||||
clearInterval(this.interval);
|
||||
this.clearTalk();
|
||||
@@ -665,7 +664,7 @@ export default {
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/deep/ .el-scrollbar__wrap {
|
||||
:deep(.el-scrollbar__wrap ) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.user-status{
|
||||
@@ -689,7 +688,7 @@ export default {
|
||||
flex-shrink: 0;
|
||||
height: 40px;
|
||||
|
||||
/deep/ .el-input .el-input__inner {
|
||||
:deep(.el-input .el-input__inner ) {
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
destroyed() {
|
||||
beforeUnmount() {
|
||||
clearInterval(this.setInterval)
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -9,17 +9,6 @@ function resolve(dir) {
|
||||
return path.join(__dirname, dir)
|
||||
}
|
||||
|
||||
const assetsCDN = {
|
||||
externals: {
|
||||
vue: 'Vue',
|
||||
|
||||
},
|
||||
css: [],
|
||||
js: [
|
||||
'https://cdn.pickmall.cn/cdn/vue.min.js',
|
||||
],
|
||||
}
|
||||
|
||||
// vue.config.js
|
||||
const vueConfig = {
|
||||
// 公共路径(必须有的)
|
||||
@@ -57,37 +46,24 @@ const vueConfig = {
|
||||
}),
|
||||
],
|
||||
|
||||
// if prod, add externals
|
||||
externals: isProd ? assetsCDN.externals : {},
|
||||
},
|
||||
|
||||
chainWebpack: config => {
|
||||
config.resolve.alias.set('@', resolve('src'))
|
||||
|
||||
const svgRule = config.module.rule('svg')
|
||||
svgRule.uses.clear()
|
||||
svgRule
|
||||
.oneOf('inline')
|
||||
.resourceQuery(/inline/)
|
||||
.use('vue-svg-icon-loader')
|
||||
.loader('vue-svg-icon-loader')
|
||||
config.module.rule('svg').exclude.add(resolve('src/icons/svg')).end()
|
||||
|
||||
config.module
|
||||
.rule('icons')
|
||||
.test(/\.svg$/)
|
||||
.include.add(resolve('src/icons/svg'))
|
||||
.end()
|
||||
.end()
|
||||
.oneOf('external')
|
||||
.use('file-loader')
|
||||
.loader('file-loader')
|
||||
.use('svg-sprite-loader')
|
||||
.loader('svg-sprite-loader')
|
||||
.options({
|
||||
name: 'assets/[name].[hash:8].[ext]',
|
||||
symbolId: 'icon-[name]',
|
||||
})
|
||||
|
||||
// if prod is on
|
||||
// assets require on cdn
|
||||
if (isProd) {
|
||||
config.plugin('html').tap(args => {
|
||||
args[0].cdn = assetsCDN
|
||||
return args
|
||||
})
|
||||
}
|
||||
},
|
||||
pluginOptions: {
|
||||
'style-resources-loader': {
|
||||
|
||||
Reference in New Issue
Block a user