|
|
|
|
@ -0,0 +1,526 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div>
|
|
|
|
|
<!-- <div class="onlineConsultation" @click="consultation">-->
|
|
|
|
|
<!-- <div class="btn">-->
|
|
|
|
|
<!-- <img :src="customerService" alt="" class="customerService">-->
|
|
|
|
|
<!-- <span class="text">在线咨询</span>-->
|
|
|
|
|
<!-- </div>-->
|
|
|
|
|
<!-- </div>-->
|
|
|
|
|
<div class="chat" v-if="isChat" ref="dragDiv">
|
|
|
|
|
<div class="topDrag" @mousedown="onMouseDown">
|
|
|
|
|
<span class="chatTitle">海威 AI 助手</span>
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="selectedModel"
|
|
|
|
|
size="mini"
|
|
|
|
|
class="modelSelect"
|
|
|
|
|
placeholder="模型"
|
|
|
|
|
@mousedown.native.stop>
|
|
|
|
|
<el-option label="DeepSeek" value="deepseek"></el-option>
|
|
|
|
|
<el-option label="Ollama" value="ollama"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
<i class="el-icon-close close" @click="isChat = false"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="content1" ref="chatContainer">
|
|
|
|
|
<template v-for="i in chatInfo">
|
|
|
|
|
<div v-if="i.type===0" class="message">
|
|
|
|
|
<div class="userInfo">{{ i.userName }} {{ formatTime(i.time) }}</div>
|
|
|
|
|
<div v-if="i.html" class="info" @click="handleClick" v-html="i.content"></div>
|
|
|
|
|
<div v-else class="info plain">{{ i.content }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="i.type===1" class="message1">
|
|
|
|
|
<div class="userInfo">{{ i.userName }} {{ formatTime(i.time) }}</div>
|
|
|
|
|
<div class="info plain">{{ i.content }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="i.type===2" class="message">
|
|
|
|
|
<div class="userInfo">{{ i.userName }} {{ formatTime(i.time) }}</div>
|
|
|
|
|
<div class="info">
|
|
|
|
|
<div>
|
|
|
|
|
请选择问题类型
|
|
|
|
|
</div>
|
|
|
|
|
<el-button size="mini" @click="busy(1)">设备相关</el-button>
|
|
|
|
|
<el-button size="mini" @click="busy(2)">软件相关</el-button>
|
|
|
|
|
<el-button size="mini" @click="busy(3)">测试</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="chatBox">
|
|
|
|
|
<el-input
|
|
|
|
|
type="textarea"
|
|
|
|
|
placeholder="请输入内容"
|
|
|
|
|
class="no-border-textarea"
|
|
|
|
|
@keydown.enter.native.exact.prevent="sendChat"
|
|
|
|
|
v-model="textarea"
|
|
|
|
|
:disabled="isLoading">
|
|
|
|
|
</el-input>
|
|
|
|
|
<el-button type="primary" size="mini" class="chatBtn" @click="sendChat" :disabled="isLoading">发送消息
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="viewImg" v-if="imgUrl" @click="imgUrl = ''">
|
|
|
|
|
<img :src="imgUrl" alt="" class="img"/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<script>
|
|
|
|
|
import customerService from '@/assets/icon/customerService.png'
|
|
|
|
|
import { aiChat } from '@/api/aiChat'
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: "Chat",
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
imgUrl: '',
|
|
|
|
|
customerService,
|
|
|
|
|
textarea: '',
|
|
|
|
|
isChat: false,
|
|
|
|
|
isLoading: false,
|
|
|
|
|
selectedModel: 'deepseek',
|
|
|
|
|
isDown: false,
|
|
|
|
|
offsetX: 0,
|
|
|
|
|
offsetY: 0,
|
|
|
|
|
chatInfo: [
|
|
|
|
|
{
|
|
|
|
|
type: 0,
|
|
|
|
|
content: '您好!我是海威 AI 助手,可以回答关于海威物联产品、方案和服务的问题。',
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '海威物联',
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
mounted() {
|
|
|
|
|
// listHwWebDocument()
|
|
|
|
|
// setTimeout(() => {
|
|
|
|
|
// this.isChat = true;
|
|
|
|
|
// this.$nextTick(() => {
|
|
|
|
|
// this.scrollToBottom()
|
|
|
|
|
// })
|
|
|
|
|
// }, 5 * 1000)
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
consultation() {
|
|
|
|
|
this.isChat = true;
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom()
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
onMouseDown(e) {
|
|
|
|
|
this.isDown = true;
|
|
|
|
|
const rect = this.$refs.dragDiv.getBoundingClientRect();
|
|
|
|
|
this.offsetX = e.clientX - rect.left;
|
|
|
|
|
this.offsetY = e.clientY - rect.top;
|
|
|
|
|
|
|
|
|
|
document.addEventListener("mousemove", this.onMouseMove);
|
|
|
|
|
document.addEventListener("mouseup", this.onMouseUp);
|
|
|
|
|
},
|
|
|
|
|
onMouseMove(e) {
|
|
|
|
|
if (!this.isDown) return;
|
|
|
|
|
|
|
|
|
|
const dragDiv = this.$refs.dragDiv;
|
|
|
|
|
|
|
|
|
|
// 鼠标相对窗口的 left/top
|
|
|
|
|
let left = e.clientX - this.offsetX;
|
|
|
|
|
let top = e.clientY - this.offsetY;
|
|
|
|
|
|
|
|
|
|
// ✅ 边界限制 (保证不出窗口)
|
|
|
|
|
if (left < 0) left = 0;
|
|
|
|
|
if (top < 0) top = 0;
|
|
|
|
|
if (left > window.innerWidth - dragDiv.offsetWidth) {
|
|
|
|
|
left = window.innerWidth - dragDiv.offsetWidth;
|
|
|
|
|
}
|
|
|
|
|
if (top > window.innerHeight - dragDiv.offsetHeight) {
|
|
|
|
|
top = window.innerHeight - dragDiv.offsetHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ✅ 转换为 right/bottom
|
|
|
|
|
let right = window.innerWidth - left - dragDiv.offsetWidth;
|
|
|
|
|
let bottom = window.innerHeight - top - dragDiv.offsetHeight;
|
|
|
|
|
|
|
|
|
|
dragDiv.style.right = right + "px";
|
|
|
|
|
dragDiv.style.bottom = bottom + "px";
|
|
|
|
|
},
|
|
|
|
|
onMouseUp() {
|
|
|
|
|
this.isDown = false;
|
|
|
|
|
document.removeEventListener("mousemove", this.onMouseMove);
|
|
|
|
|
document.removeEventListener("mouseup", this.onMouseUp);
|
|
|
|
|
},
|
|
|
|
|
scrollToBottom() {
|
|
|
|
|
const container = this.$refs.chatContainer;
|
|
|
|
|
container.scrollTop = container.scrollHeight;
|
|
|
|
|
},
|
|
|
|
|
formatTime(date) {
|
|
|
|
|
const now = new Date();
|
|
|
|
|
const input = new Date(date);
|
|
|
|
|
|
|
|
|
|
const diffTime = now - input;
|
|
|
|
|
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
|
|
|
|
|
|
|
|
|
const isToday = now.toDateString() === input.toDateString();
|
|
|
|
|
|
|
|
|
|
const yesterday = new Date();
|
|
|
|
|
yesterday.setDate(now.getDate() - 1);
|
|
|
|
|
const isYesterday = yesterday.toDateString() === input.toDateString();
|
|
|
|
|
const padZero = num => String(num).padStart(2, '0');
|
|
|
|
|
const timeStr = `${padZero(input.getHours())}:${padZero(input.getMinutes())}:${padZero(input.getSeconds())}`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (isToday) {
|
|
|
|
|
return `今天 ${timeStr}`;
|
|
|
|
|
} else if (isYesterday) {
|
|
|
|
|
return `昨天 ${timeStr}`;
|
|
|
|
|
} else {
|
|
|
|
|
return `${diffDays}天前`;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async sendChat(e) {
|
|
|
|
|
if (e && e.isComposing) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const question = this.textarea.trim()
|
|
|
|
|
if (question === '' || this.isLoading) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
this.chatInfo.push({
|
|
|
|
|
type: 1,
|
|
|
|
|
content: question,
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '我',
|
|
|
|
|
})
|
|
|
|
|
this.textarea = ''
|
|
|
|
|
this.isLoading = true
|
|
|
|
|
const loadingIndex = this.chatInfo.push({
|
|
|
|
|
type: 0,
|
|
|
|
|
content: '正在检索官网知识库并生成回答...',
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '海威物联',
|
|
|
|
|
}) - 1
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom()
|
|
|
|
|
})
|
|
|
|
|
try {
|
|
|
|
|
// AI 输出一律按纯文本展示,避免模型返回 HTML 时进入 v-html 渲染链路。
|
|
|
|
|
const res = await aiChat({
|
|
|
|
|
question,
|
|
|
|
|
model: this.selectedModel
|
|
|
|
|
})
|
|
|
|
|
if (res && res.code === 200 && res.data) {
|
|
|
|
|
this.chatInfo.splice(loadingIndex, 1, {
|
|
|
|
|
type: 0,
|
|
|
|
|
content: res.data.answer || '官网知识库暂未检索到相关信息。',
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '海威物联',
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
this.chatInfo.splice(loadingIndex, 1, {
|
|
|
|
|
type: 0,
|
|
|
|
|
content: (res && res.msg) || 'AI 服务暂时不可用,请稍后重试。',
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '海威物联',
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
this.chatInfo.splice(loadingIndex, 1, {
|
|
|
|
|
type: 0,
|
|
|
|
|
content: '网络连接异常,请稍后重试。',
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '海威物联',
|
|
|
|
|
})
|
|
|
|
|
} finally {
|
|
|
|
|
this.isLoading = false
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
busy(e) {
|
|
|
|
|
if (e === 1) {
|
|
|
|
|
this.chatInfo.push({
|
|
|
|
|
type: 0,
|
|
|
|
|
content: '请稍后',
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '海威物联',
|
|
|
|
|
})
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.chatInfo.push({
|
|
|
|
|
type: 0,
|
|
|
|
|
content: `<text>目前咨询人数过多,如有疑问,请致电xxxx,或</text> <a href="javascript:void(0)" data-action="addWechat">添加微信</a>`,
|
|
|
|
|
html: true,
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '海威物联',
|
|
|
|
|
})
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom()
|
|
|
|
|
})
|
|
|
|
|
}, 1000)
|
|
|
|
|
}
|
|
|
|
|
if (e === 2) {
|
|
|
|
|
this.chatInfo.push({
|
|
|
|
|
type: 0,
|
|
|
|
|
content: '请稍后',
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '海威物联',
|
|
|
|
|
})
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.chatInfo.push({
|
|
|
|
|
type: 0,
|
|
|
|
|
content: '目前咨询人数过多,如有疑问,请致电xxxx',
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '海威物联',
|
|
|
|
|
})
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom()
|
|
|
|
|
})
|
|
|
|
|
}, 1000)
|
|
|
|
|
}
|
|
|
|
|
if (e === 3) {
|
|
|
|
|
window.location.href = "weixin://dl/add";
|
|
|
|
|
}
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom()
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleClick(e) {
|
|
|
|
|
const action = e.target.dataset.action
|
|
|
|
|
if (action && typeof this[action] === 'function') {
|
|
|
|
|
this[action](e.target.dataset.parmas) // 调用 Vue methods 里的函数
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
copyToClipboard(text) {
|
|
|
|
|
if (!navigator.clipboard) {
|
|
|
|
|
console.warn('浏览器不支持 Clipboard API,使用 fallback 方法')
|
|
|
|
|
this.fallbackCopy(text)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
navigator.clipboard.writeText(text)
|
|
|
|
|
},
|
|
|
|
|
fallbackCopy(text) {
|
|
|
|
|
const input = document.createElement('textarea')
|
|
|
|
|
input.value = text
|
|
|
|
|
document.body.appendChild(input)
|
|
|
|
|
input.select()
|
|
|
|
|
try {
|
|
|
|
|
document.execCommand('copy')
|
|
|
|
|
} catch (err) {
|
|
|
|
|
}
|
|
|
|
|
document.body.removeChild(input)
|
|
|
|
|
},
|
|
|
|
|
addWechat() {
|
|
|
|
|
window.location.href = "weixin://dl/add";
|
|
|
|
|
this.copyToClipboard('Yeshenge_')
|
|
|
|
|
this.chatInfo.push({
|
|
|
|
|
type: 0,
|
|
|
|
|
content: '已复制微信号,请打开微信添加好友,或扫描下方二维码添加好友',
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '海威物联',
|
|
|
|
|
})
|
|
|
|
|
let img = require('@/assets/image/wechatQRCode.png')
|
|
|
|
|
this.chatInfo.push({
|
|
|
|
|
type: 0,
|
|
|
|
|
content: `<img data-action="viewImg" data-parmas='${JSON.stringify({img: img})}' src="${img}" alt="" style="width: 100px;">`,
|
|
|
|
|
html: true,
|
|
|
|
|
time: new Date().getTime(),
|
|
|
|
|
userName: '海威物联',
|
|
|
|
|
})
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom()
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
viewImg(e) {
|
|
|
|
|
let data = JSON.parse(e)
|
|
|
|
|
console.log(data.img)
|
|
|
|
|
this.imgUrl = data.img
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
<style scoped lang="less">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.onlineConsultation {
|
|
|
|
|
position: fixed;
|
|
|
|
|
right: 5vw;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
width: 150px;
|
|
|
|
|
height: 50px;
|
|
|
|
|
background-color: #41B5EA;
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
z-index: 99;
|
|
|
|
|
|
|
|
|
|
.customerService {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: 70px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.text {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
left: 70px;
|
|
|
|
|
width: 80px;
|
|
|
|
|
line-height: 50px;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat {
|
|
|
|
|
z-index: 999;
|
|
|
|
|
position: fixed;
|
|
|
|
|
bottom: 5vw;
|
|
|
|
|
right: 5vw;
|
|
|
|
|
width: 450px;
|
|
|
|
|
height: 450px;
|
|
|
|
|
border-top-left-radius: 5px;
|
|
|
|
|
border-top-right-radius: 5px;
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
box-shadow: -4px 4px 8px rgba(0, 0, 0, 0.1),
|
|
|
|
|
4px 4px 8px rgba(0, 0, 0, 0.1),
|
|
|
|
|
0 4px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
|
|
|
|
.topDrag {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 50px;
|
|
|
|
|
background-color: #41B5EA;
|
|
|
|
|
cursor: move;
|
|
|
|
|
|
|
|
|
|
.chatTitle {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 50%;
|
|
|
|
|
left: 16px;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
color: #fff;
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.modelSelect {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 50%;
|
|
|
|
|
right: 54px;
|
|
|
|
|
width: 104px;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
cursor: default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.close {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 50%;
|
|
|
|
|
right: 20px;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
color: #fff;
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.content1 {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 50px;
|
|
|
|
|
height: 300px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
overflow: auto;
|
|
|
|
|
|
|
|
|
|
.message {
|
|
|
|
|
width: 80%;
|
|
|
|
|
margin: 10px 0 10px 15px;
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
|
|
|
|
.userInfo {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: #0008
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info {
|
|
|
|
|
max-width: 80%;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
background-color: #EDEFF5;
|
|
|
|
|
padding: 5px;
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
border-top-left-radius: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.plain {
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message1 {
|
|
|
|
|
width: calc(100% - 20px);
|
|
|
|
|
margin: 10px 0 10px 5px;
|
|
|
|
|
text-align: right;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
|
|
.userInfo {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: #0008
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info {
|
|
|
|
|
text-align: left;
|
|
|
|
|
max-width: 80%;
|
|
|
|
|
float: right;
|
|
|
|
|
background-color: #EDEFF5;
|
|
|
|
|
padding: 5px;
|
|
|
|
|
border-radius: 5px 0px 5px 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.plain {
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chatBox {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 350px;
|
|
|
|
|
height: 88px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
border-top: 2px solid #2222;
|
|
|
|
|
|
|
|
|
|
.no-border-textarea /deep/ .el-textarea__inner {
|
|
|
|
|
border: none;
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
resize: none;
|
|
|
|
|
outline: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chatBtn {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: 5px;
|
|
|
|
|
right: 10px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.viewImg {
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
position: fixed;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
background-color: rgba(0, 0, 0, .2);
|
|
|
|
|
|
|
|
|
|
.img {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 50%;
|
|
|
|
|
left: 50%;
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
width: 40vw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|