提交 cc9b12b0 作者: Hao

合并代码

父级 749e555b
......@@ -8,12 +8,19 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
AutomaticPrompt: typeof import('./src/components/AutomaticPrompt.vue')['default']
ElAside: typeof import('element-plus/es')['ElAside']
ElButton: typeof import('element-plus/es')['ElButton']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElFooter: typeof import('element-plus/es')['ElFooter']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElMain: typeof import('element-plus/es')['ElMain']
ElOption: typeof import('element-plus/es')['ElOption']
ElSelect: typeof import('element-plus/es')['ElSelect']
EmotionComponent: typeof import('./src/components/EmotionComponent.vue')['default']
IndexComponent: typeof import('./src/components/IndexComponent.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
......
......@@ -102,6 +102,12 @@ section {
display: block;
}
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
line-height: 1;
}
......
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1712030397294" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12884" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M603.75168 775.371386 539.791973 775.371386 320.738955 328.101643 234.988965 328.101643 234.988965 293.685809 303.81961 293.685809 441.416432 293.685809 480.703203 293.685809 598.688361 594.7077 716.674543 293.685809 819.919998 293.685809Z" fill="#1296db" p-id="12885"></path></svg>
\ No newline at end of file
......@@ -21,7 +21,7 @@
alt=""
/>
</div>
<div class="chat-bar-right">
<!-- <div class="chat-bar-right">
<img
src="../assets/icon_folder_s1_01.png"
@click="upfile"
......@@ -29,7 +29,7 @@
alt=""
/>
快捷回复
</div>
</div> -->
</div>
<div
class="el-autocomplete textarea"
......
......@@ -19,3 +19,11 @@ export function getShortDate(): string {
return month + "-" + day + " " + hour + ":" + minutes;
}
export const require = (imgPath: any) => {
try {
const handlePath = imgPath.replace("@", "..");
return new URL(handlePath, import.meta.url).href;
} catch (error) {
console.warn(error);
}
};
<template>
<div class="common-layout">
<!-- v-if="customerInfo.username" -->
<el-container>
<el-container class="el-container-center" v-if="customerInfo.username">
<el-header class="el-header-center">
<p class="onlineCustomer">您正在与 在线客服 对话</p>
</el-header>
<el-main
id="srollId"
ref="srollId"
:style="{
height: '60vh',
width: '100%',
border: '1px solid #ccc',
}"
>
<div class="title">您好,系统正在为您接入在线客服</div>
<div
class="message-container"
v-for="(message, index) in messages"
:key="index"
:class="getMessageClass(message?.isSent)"
<el-header class="el-header-center">
<p class="onlineCustomer">You are talking to online customer service</p>
</el-header>
<el-container>
<el-container>
<el-main
id="srollId"
ref="srollId"
:style="{
padding: '24px',
height: '60vh',
width: '100%',
}"
>
<div class="time">{{ message.time }}</div>
<div v-if="message.isSent" class="message-container">
<div class="bubble">
<div
v-if="message?.msgType == 0"
class="message"
v-html="message?.content"
@click.prevent="handleMessageClick($event)"
></div>
<div v-else class="img-wraper">
<el-image
:src="message?.content"
:preview-src-list="[message?.content]"
/>
<div class="title-message">You have no history messages</div>
<div class="message-container message-container-right">
<div class="classSendLink">
<div class="classSendDiv">
<div class="classSend-img">
<img :src="productInfo.productImg" />
</div>
<div class="classSend-price">
<div class="class-send-title">
{{ productInfo.productName }}
</div>
<div class="class-send-box">
<div class="class-send-price">
{{ productInfo.price }}
</div>
<!-- <div class="send">{{ productInfo.link }}</div> -->
</div>
</div>
</div>
<div class="sendlinkbox" @click="toSendLink(productInfo)">
<div class="box-send">send link</div>
<div class="box-close">Close</div>
</div>
</div>
<div class="avatar">
<img
src="../assets/logo.png"
alt="Avatar"
class="avatar-image"
/>
</div>
</div>
<div v-if="!message.isSent" class="message-container">
<div class="avatar">
<img :src="message?.userImg" class="avatar-image" />
<div v-for="(message, index) in messages" :key="index">
<div
class="message-container message-container-right"
v-if="message.msgType == 3"
@click.prevent="handleMessageClick($event)"
>
<div class="classSendLink">
<div class="classSendDiv">
<div class="classSend-img">
<img :src="message.content.productImg" />
</div>
<div class="classSend-price">
<div class="class-send-title">
{{ message.content.productName }}
</div>
<div class="class-send-box">
<div class="class-send-price">
{{ message.content.price }}
</div>
<!-- <div class="send">{{ message.content.link }}</div> -->
</div>
</div>
</div>
<!-- <div class="sendlinkbox" @click="toSendLink">
send link
<img src="../assets/right.png" />
</div> -->
</div>
</div>
<div class="bubble">
<div
v-if="message?.msgType == 0"
class="message"
v-html="message?.content"
@click.prevent="handleMessageClick($event)"
></div>
<div v-else class="img-wraper">
<el-image
:src="message?.content"
:preview-src-list="[message?.content]"
/>
<div
v-else
class="message-container"
:class="getMessageClass(message?.isSent)"
>
<div class="time">{{ message.time }}</div>
<div v-if="message.isSent" class="message-container">
<div class="bubble">
<!--文字 -->
<div
v-if="message?.msgType == 0"
class="message"
v-html="message?.content"
@click.prevent="handleMessageClick($event)"
></div>
<div v-else class="img-wraper">
<el-image
:src="message?.content"
:preview-src-list="[message?.content]"
/>
</div>
</div>
<div class="avatar">
<img
src="../assets/shop.jpg"
alt="Avatar"
class="avatar-image"
/>
</div>
</div>
<div v-if="!message.isSent" class="message-container">
<div class="avatar">
<img :src="message?.userImg" class="avatar-image" />
</div>
<div class="bubble">
<div
v-if="message?.msgType == 0"
class="message"
v-html="message?.content"
@click.prevent="handleMessageClick($event)"
></div>
<div v-else class="img-wraper">
<el-image
:src="message?.content"
:preview-src-list="[message?.content]"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</el-main>
<el-footer class="el-footer">
<div class="static-box">
<div
v-for="(item, index) in staticList"
:key="index"
class="staticList"
>
<div>{{ item }}</div>
</el-main>
<el-footer class="el-footer" :style="{ border: '1px solid #E9ECF1' }">
<div class="static-box">
<div
v-for="(item, index) in staticList"
:key="index"
class="staticList"
>
<div>{{ item }}</div>
</div>
</div>
<AutomaticPrompt
@keydown.enter="handleButtonClick"
@updateState="getState"
ref="automaticPromptRef"
></AutomaticPrompt>
<div class="btn-send">
<div>[ Send shortcut key: Enter ]</div>
<div>
<el-button
plain
style="width: 50px; background: #f8f8fa; color: #000;"
@click.stop="handleButtonClick"
>
Finish
</el-button>
<el-button
style="width: 50px; color: #fff; background: #000;"
plain
@click.stop="handleButtonClick"
>
send
</el-button>
</div>
</div>
</el-footer>
</el-container>
<el-aside
v-if="num == 1"
width="292px"
:style="{
height: 'calc(100vh - 60px)',
}"
>
<div class="recentlyViewd">最近浏览</div>
<div
v-for="(item, index) in productList"
:key="index"
class="browse-glance"
>
<img :src="item.productImg" class="browse-img" />
<div class="browse-glowup">
<div class="title">{{ item.productName }}</div>
<div class="units">
<div class="price">{{ item.price }}</div>
<div class="send" @click="toSendLink(item)">
{{ item.link }}
</div>
</div>
</div>
</div>
<AutomaticPrompt
@keydown.enter="handleButtonClick"
@updateState="getState"
ref="automaticPromptRef"
></AutomaticPrompt>
<div class="btn-send">
<el-button
plain
style="width: 50px; background: #f8f8fa; color: #000;"
@click.stop="handleButtonClick"
>
Finish
</el-button>
<el-button
style="width: 50px; color: #fff; background: #000;"
plain
@click.stop="handleButtonClick"
</el-aside>
<el-aside
width="292px"
v-if="num == 2"
:style="{
height: 'calc(100vh - 60px)',
}"
>
<div class="recentlyViewd">Request for Quotations</div>
<div class="recently-input-box">
<el-form
label-position="top"
style="max-width: 600px;"
label-width="auto"
class="demo-dynamic"
ref="ruleFormRef"
:rules="rules"
:model="ruleForm"
>
send
</el-button>
<el-form-item prop="ProductName" label="Product Name">
<el-input
v-model="ruleForm.ProductName"
style="height: 36px;"
placeholder="Enter a specific product name"
prop="Email"
/>
</el-form-item>
<el-form-item prop="Email" label="Purchase Quantity">
<el-select
placeholder="Select"
prop="Email"
style="height: 36px;"
v-model="ruleForm.Email"
>
<el-option label="Restaurant" value="1" />
<el-option label="Order No." value="2" />
<el-option label="Tel" value="3" />
</el-select>
</el-form-item>
<el-form-item
prop="explanation"
label="Other requirements explanation"
>
<el-input
style="max-width: 600px; height: 36px;"
placeholder="Please enter"
v-model="ruleForm.explanation"
class="input-with-select"
prop="Email"
>
<template #append>
<el-select
placeholder="China"
style="width: 96px;"
prop="China"
v-model="ruleForm.China"
>
<el-option label="Restaurant" value="1" />
<el-option label="Order No." value="2" />
<el-option label="Tel" value="3" />
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item
label="Other requirements explanation"
prop="explanation"
>
<el-input
v-model="ruleForm.explanation"
:autosize="{ minRows: 3, maxRows: 10 }"
type="textarea"
placeholder="Please enter"
/>
</el-form-item>
<el-form-item>
<div class="requirements-box" v-if="!isUpload">
<el-button icon="UploadFilled" class="uploadInfo" />
<div class="upload">
<div class="upload-name">Click to upload</div>
<div>or drag and drap</div>
</div>
</div>
<div class="requirements-box1" v-if="isUpload">
<div class="box">
<div class="upload-name">Tech design requirements.pdf</div>
<div class="upload-info">
<el-icon color="green"><CircleCheck /></el-icon>
<span>200KB</span>
</div>
</div>
<el-icon :size="20"><Delete /></el-icon>
</div>
</el-form-item>
<el-form-item prop="EmailAddress" label="Email Address">
<el-input style="height: 44px;" placeholder="Please enter" v-model="ruleForm.EmailAddress" />
</el-form-item>
<div class="footerSubmit">Submit</div>
</el-form>
</div>
</el-footer>
</el-aside>
</el-container>
<el-container class="el-container-center" v-else></el-container>
</el-container>
</div>
</template>
<script lang="ts" setup>
import '../assets/font/iconfont.css'
import { ElMessage } from 'element-plus'
import { ref, onMounted, watch, nextTick, getCurrentInstance } from 'vue'
import type { FormInstance } from 'element-plus'
import {
ref,
onMounted,
watch,
nextTick,
getCurrentInstance,
reactive,
} from 'vue'
import { getShortDate } from '../utils/index'
import { useUserStore } from '../store/modules/user'
import AutomaticPrompt from '../components/AutomaticPrompt.vue'
......@@ -115,8 +307,53 @@ import { sendWebSocket } from '../utils/websocket'
import { initWebSocket } from '../utils/websocket'
import '@luohc92/vue3-image-viewer/dist/style.css'
import { checkMesssages } from '../axios/model/user'
import { require } from '@/utils/index'
const ruleForm = ref({
count: 1,
})
const rules = reactive({
ProductName: [
{
required: true,
message: 'Enter a specific product name',
trigger: 'blur',
},
],
Email: [
{
required: true,
message: 'Selct',
trigger: 'blur',
},
],
explanation: [
{
required: true,
message: 'Please enter',
trigger: 'blur',
},
],
China: [
{
required: true,
message: 'Please enter',
trigger: 'blur',
},
],
EmailAddress: [
{
required: true,
message: 'Please enter',
trigger: 'blur',
},
],
})
const ruleFormRef = ref<FormInstance>()
const customerInfo = ref({})
const messages = ref([])
const num = 2
const isUpload = true
const staticList = ref([
'Login account',
'Forgot password',
......@@ -124,6 +361,32 @@ const staticList = ref([
'Add/manage products',
'improve',
])
const productInfo = ref({
productImg: require('../assets/shop.jpg'),
productName: 'The secret to looking glowup nowadays',
price: '$1.40 - 2.50',
link: 'send',
})
const productList = ref([
{
productImg: require('../assets/camera.png'),
productName: 'The secret to looking glowup nowadays',
price: '$1.35 - 2.39',
link: 'send',
},
{
productImg: require('../assets/camera.png'),
productName: 'The secret to looking glowup nowadays',
price: '$1.30 - 2.00',
link: 'send',
},
{
productImg: require('../assets/camera.png'),
productName: 'The secret to looking glowup nowadays',
price: '$1.00 - 2.20',
link: 'send',
},
])
const store = useUserStore()
const automaticPromptRef = ref('')
let msg = ''
......@@ -157,9 +420,7 @@ const getHistoryMessage = () => {
group_id: '',
userId: store.userInfo.username,
}
console.log(data)
sendWebSocket(data)
// getCheckMesssages(data)
}
//设置message
......@@ -171,9 +432,6 @@ const setMessage = () => {
const getState = (v: string) => {
msg = v
}
const handleChange = (val: any) => {
store.setCount(val)
}
//监听聊天框数据的改变
watch(
messages,
......@@ -219,8 +477,6 @@ const handleButtonClick = () => {
const handleMessageClick = (event: any) => {
const target = event.target
if (target.tagName === 'A') {
// 点击的是超链接
// 执行相应的操作
if (target.innerHTML === '解决') {
alert('感谢您的使用')
} else if (target.innerHTML === '未解决') {
......@@ -261,33 +517,97 @@ const handleLinkClick = (msg: string) => {
const getMessageClass = (isSent: boolean) => {
return isSent ? 'message-container-right' : 'message-container-left'
}
const toSendLink = (e: any) => {
const data = {
content: e,
isSent: true,
cmd: '11',
msgType: 3,
chatType: '2',
fromLang: 'cn',
toLang: 'en',
group_id: '',
time: getShortDate(),
to: customerInfo.value.username,
form: store.userInfo.username,
}
messages.value?.push(data)
sendWebSocket(data)
}
</script>
<style lang="scss" scoped>
.title {
.title-message {
color: #798494;
text-align: center;
font-feature-settings: 'clig' off, 'liga' off;
font-family: 'Helvetica Neue';
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 16px;
}
.class-send-box {
display: flex;
padding: 8px;
justify-content: center;
align-items: center;
border-radius: 4px;
width: 196px;
background: #f8f8fa;
font-size: 12px;
font-family: 'PingFang SC';
justify-content: space-between;
.send {
justify-content: center;
align-items: center;
border-radius: 4px;
border: 1px solid #c7cdd4;
color: #031327;
font-family: 'PingFang SC';
font-size: 12px;
width: 56px;
font-weight: 400;
line-height: 20px;
text-align: center;
}
}
.class-send-title {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
align-self: stretch;
overflow: hidden;
color: #0c203d;
text-overflow: ellipsis;
font-family: 'Helvetica Neue';
font-size: 14px;
font-style: normal;
font-weight: 400;
margin: 0px auto;
color: #798494;
line-height: 20px;
}
.onlineCustomer {
.classSend-price {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.class-send-price {
color: #0c203d;
font-feature-settings: 'clig' off, 'liga' off;
font-family: 'Helvetica Neue';
font-size: 18px;
font-style: normal;
font-weight: 700;
line-height: 24px;
letter-spacing: 0.72px;
}
.recentlyViewd {
display: flex;
height: 44px;
width: 100%;
font-size: 14px;
padding: 8px;
line-height: 44px;
align-items: center;
gap: 8px;
background: rgba(248, 248, 250, 1);
background: #f8f8faff;
}
.onlineCustomer {
color: #010914;
font-family: 'PingFang SC';
font-size: 14px;
font-style: normal;
font-weight: 600;
font-weight: 400;
}
.el-header-left {
height: 76px;
......@@ -316,8 +636,14 @@ const getMessageClass = (isSent: boolean) => {
padding: 0px;
}
.el-header-center {
border: 1px solid #e6e8ed;
border-top: 1px solid #e6e8ed;
border-bottom: 1px solid #e6e8ed;
border-right: 1px solid #e6e8ed;
display: flex;
height: 44px;
line-height: 44px;
background: rgba(248, 248, 250, 1);
padding-left: 8px;
justify-content: space-between;
flex-direction: column;
.name {
......@@ -353,33 +679,46 @@ const getMessageClass = (isSent: boolean) => {
}
.el-footer {
width: 100%;
border: 1px solid #ccc;
border-top: 0px;
padding: 0px;
height: calc(100vh - 60vh - 60px);
height: calc(100vh - 60vh - 44px);
position: relative;
.btn-send {
display: flex;
justify-content: space-between;
align-items: center;
bottom: 10px;
position: absolute;
bottom: 20px;
right: 20px;
width: 100%;
padding: 18px 21px;
padding-bottom: 0px;
div {
&:nth-of-type(1) {
font-family: 'PingFang SC';
font-size: 14px;
font-style: normal;
font-weight: 400;
}
}
}
.static-box {
display: flex;
background: #e6e8ed;
background: #f8f8faff;
padding: 8px 16px;
height: 52px;
}
.staticList {
height: 36px;
border: 1px solid #e6e8ed;
border-radius: 36px;
background: #fff;
width: max-width;
width: max-content;
color: #000000;
padding: 0px 16px;
font-family: 'Inter';
font-size: 14px;
font-weight: 600;
font-weight: 500;
line-height: 36px;
text-align: center;
margin-right: 8px;
......@@ -721,4 +1060,214 @@ const getMessageClass = (isSent: boolean) => {
background: #f2f3f5;
}
}
#srollId {
border-right: 1px solid #e6e8ed;
}
.browse-glance {
display: flex;
padding: 16px 12px 0px 12px;
.browse-img {
width: 64px;
height: 64px;
margin-right: 12px;
}
.units {
margin-top: 8px;
color: #0c203d;
font-feature-settings: 'clig' off, 'liga' off;
font-family: 'Helvetica Neue';
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px;
display: flex;
.price {
color: #0c203d;
font-feature-settings: 'clig' off, 'liga' off;
font-family: 'Helvetica Neue';
font-size: 16px;
font-style: normal;
font-weight: 700;
line-height: 24px;
letter-spacing: 0.72px;
}
.send {
display: flex;
width: 56px;
margin-left: 30px;
height: var(--Layout-md, 24px);
justify-content: center;
align-items: center;
border-radius: 4px;
border: 1px solid #c7cdd4;
}
}
.title {
display: flex;
padding: 8px;
justify-content: center;
align-items: center;
border-radius: 4px;
background: #f8f8fa;
font-size: 12px;
font-family: 'PingFang SC';
font-weight: 400;
margin: 0px auto;
color: #798494;
}
}
.classSendLink {
.classSend-img {
width: 72px;
height: 72px;
margin-right: 12px;
img {
width: 100%;
height: 100%;
}
}
.classSendDiv {
display: flex;
padding-bottom: 8px;
}
width: 360px;
padding: 12px 12px 0px 12px;
border-radius: 8px 0 8px 8px;
border: 1px solid #e9ecf1;
}
.sendlinkbox {
height: 40px;
line-height: 40px;
display: flex;
justify-content: center;
border-top: 1px solid #e9ecf1;
.box-send,
.box-close {
color: #626d7a;
font-family: 'Helvetica Neue';
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px;
}
img {
width: 16px;
height: 16px;
margin-left: 8px;
}
display: flex;
align-items: center;
color: #626d7a;
font-feature-settings: 'clig' off, 'liga' off;
font-family: 'Helvetica Neue';
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px;
}
.sendlinkbox {
div {
display: flex;
align-items: center;
justify-content: center;
}
.box-send {
width: 100%;
line-height: 40px;
}
.box-close {
width: 100%;
line-height: 40px;
position: relative;
&::before {
content: ' ';
width: 1px;
background: #e9ecf1ff;
height: 24px;
position: absolute;
left: 0px;
}
}
}
.recently-input-box {
padding: 12px;
}
.requirements-box,
.requirements-box1 {
.box {
display: flex;
justify-content: space-between;
flex-direction: column;
.upload-info {
align-items: center;
display: flex;
}
}
display: flex;
width: 100%;
padding: 14px 12px;
height: 68px;
box-sizing: border-box;
align-items: center;
justify-content: space-between;
.upload-name {
color: #073a3dff;
font-family: 'Inter';
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 20px;
}
.upload-info {
display: inline-block;
color: #798494ff;
text-overflow: ellipsis;
font-family: 'Inter';
font-size: 14px;
display: flex;
align-items: center;
font-style: normal;
font-weight: 400;
line-height: 20px;
span {
margin-left: 6px;
}
}
}
.requirements-box {
border: 1px solid #eaecf0;
padding: 12px 58px 12px 14px;
}
.requirements-box1 {
background: #f6f9fcff;
}
.uploadInfo {
width: 40px;
height: 40px;
}
.footerSubmit {
width: 120px;
height: 36px;
line-height: 36px;
justify-content: center;
text-align: center;
margin-top: 81px;
color: #fff;
border-radius: var(--Radius-sm, 6px);
border: var(--Edges-zero, 1px) solid var(--Primary, #006970);
background: var(--Primary, #006970);
box-shadow: 0 1px 2px 0 #1018280d;
}
:deep(.el-select) {
height: 36px;
background: #ffffffff;
}
:deep(.el-select__wrapper) {
height: 36px !important;
}
</style>
......@@ -90,7 +90,6 @@ export default {
Login(data).then(({ success, message, result }: any) => {
if (success) {
router.push('/')
store.setUserInfo({
...result.userInfo,
token: result.token,
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论