
svelte-webchat一款基于svelte3.x+sveltekit+svelteLayer开发构建的仿微信PC版聊天实例。

svelteWechat 支持消息+emoj表情、图片/视频/链接预览、粘贴截图/拖拽图片发送、红包/朋友圈、全屏/更换主题等功能。

前段时间也有分享一个svelte.js移动端聊天实例。
https://cloud.tencent.com/developer/article/1976254



svelte-webchat还有朋友圈模块,下拉刷新、预览大图、侧边发布弹窗等功能。



项目整体使用到的滚动条及弹窗组件,均是基于svelte.js自定义组件实现功能。

svelte-scrollbar一款轻量级svelte.js自定义模拟美化滚动条组件


svelte-layer一款功能丰富的svelte.js自定义PC端弹窗组件
使用sveltekit构建的项目,提供了__layout.svelte公共布局及__error.svelte错误处理。

如上图:项目整体布局分为左侧+右侧内容区+底部dock菜单三个大的模块。
<div class="sv__container flexbox flex-alignc flex-justifyc" style="--themeSkin: {$skin}">
<div class="sv__wrapper" class:maximize={$isWinMaximize}>
{#if $userinfo}
<div class="sv__board flexbox flex-col">
<!-- <div class="sv__topbar">顶部模块</div> -->
<div class="sv__mainwrap flex1 flexbox">
<!-- <div class="sv__sidebar">侧边栏</div> -->
<Middle />
<div class="sv__mainbx flex1 flexbox flex-col">
<Winbar />
<slot />
</div>
</div>
<Dock />
</div>
{:else}
<div class="sv__board flexbox flex-col">
<div class="sv__mainwrap flex1 flexbox">
<slot />
</div>
</div>
{/if}
</div>
</div>
另外还提供了错误处理页面。
<!-- //Svelte错误页 -->
<script context="module">
export function load({ error, status }) {
return {
props: { error, status }
}
}
</script>
<script>
import { goto } from '$app/navigation'
export let status
export let error
function goBack() {
// history.go(-1)
goto('/')
}
</script>
<svelte:head>
<title>{status} Error!</title>
</svelte:head>
<div class="sv__scrollview flex1">
<div class="sv__page-error flexbox flex-col flex-alignc flex-justifyc">
<div class="sv__page-error-img">
<img src="404.png" alt="" />
</div>
<div class="sv__page-error-content">
<div class="c-red fs-18">┗| {status} |┛ Page Error~~</div>
<div class="c-999 mt-10">{error.message}</div>
<div class="sv__btn" on:click={goBack}><i class="iconfont icon-arrL"></i> 返回首页</div>
</div>
</div>
</div>


<script>
import { onMount } from 'svelte'
import { goto } from '$app/navigation'
import Layer, { svLayer } from '$lib/Layer'
import { userinfo } from '@/store'
import util from '@/utils'
let formObj = {}
let vcodeText = '获取验证码'
let disabled = false
let time = 0
let agree = false
let vcodeLG = true
let telEl
onMount(() => {
userinfo.useStorage()
telEl.focus()
})
function Message(content) {
svLayer.message({content, icon: 'warning', time: 0, xclose: true, shade: true})
}
// 60s倒计时
function getVcode() {
let { tel } = formObj
if(!tel) {
Message('手机号不能为空')
}else if(!util.checkTel(tel)) {
Message('手机号不正确')
}else {
time = 60
disabled = true
countDown()
}
}
function countDown() {
if(time > 0) {
vcodeText = '获取验证码('+ time +')'
time--
setTimeout(countDown, 1000)
}else {
vcodeText = '获取验证码'
time = 0
disabled = false
}
}
function handleSubmit() {
console.log('——+——+——表单数据:', formObj)
let { tel, vcode, pwd } = formObj
if(!tel) {
Message('手机号不能为空')
}else if(!util.checkTel(tel)) {
Message('手机号不正确')
}else if(vcodeLG && !vcode) {
Message('验证码不能为空')
}else if(!vcodeLG && !pwd) {
Message('密码不能为空')
}else {
svLayer({
type: 'toast',
icon: 'success',
content: '登录成功',
shadeClose: false,
time: 2,
onClose: () => {
userinfo.set({
user: tel,
token: util.setToken()
})
goto('/')
}
})
}
}
</script>








底部dock菜单支持鼠标滑过提示标题、滚轮左右滚动/拖拽滚动、菜单配置项支持path跳转、自定义img和iconfont图标(颜色+大小),点击选项支持自定义事件并返回索引。
<div class="sv__dock-scroll" style="background: {bgcolor};">
<Scrollbar autohide size={2} mousewheel>
<div class="sv__dock-wrap" bind:this={dockEl}>
<!-- dock菜单项 -->
{#each menu as item,index}
{#if item.type == 'divider'}
<div class="sv__dock-divider"></div>
{:else}
<div class="sv__dock-item flexbox" class:cur={currentTabIndex == index} id={item.id || 'dock'+index} role="{item.title}" style="color: {currentTabIndex == index ? activeColor : color}" on:click={changeTab(index, item)}>
{#if item.icon}<span class="iconfont {item.icon}" style="color: {item.color}; font-size: {item.iconSize}">{item.icon.charAt(1) == '' ? item.icon : ''}</span>{/if}
{#if item.img}<img class="iconimg" src={item.img} alt="" style="font-size: {item.iconSize};" />{/if}
{#if item.badge}<span class="sv__badge sv__dock-badge">{item.badge}</span>{/if}
{#if item.dot}<span class="sv__badge-dot sv__dock-badgeDot"></span>{/if}
</div>
{/if}
{/each}
</div>
</Scrollbar>
</div><script>
// 当前索引
export let current = 0
// 背景色
export let bgcolor = undefined
// 颜色
export let color = '#795548'
// 激活颜色
export let activeColor = '#ff3e00'
// 菜单项
export let menu = [
{
path: '/',
icon: 'icon-xiaoxi1',
// color: '#2979ff',
title: '消息',
badge: 8
},
{
path: '/friends',
icon: 'icon-haoyou',
// color: '#17c956',
title: '通讯录'
},
{
path: '/my/favor',
icon: 'icon-shoucang2',
// color: '#8d1cff',
title: '收藏'
},
{
path: '/fzone',
img: '/fzone.png',
title: '朋友圈',
dot: true
},
{ type: 'divider' },
{
img: '/svelte.png',
title: 'svelte.js',
},
{
img: '/info.png',
title: '关于',
},
{
img: '/skin.png',
title: '皮肤',
id: 'skinwrap',
},
{
icon: 'icon-filter',
color: '#673ab7',
title: '设置',
},
{
icon: 'icon-custom',
color: '#00bcd4',
iconSize: '28px',
title: '应用',
id: 'appwrap'
},
{ type: 'divider' },
{
icon: 'icon-tuichu',
color: '#607d8b',
title: '退出',
id: 'logoutwrap'
}
]
import { page } from '$app/stores'
import { goto } from '$app/navigation'
import { beforeUpdate, onMount, onDestroy, createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
import Sortable from 'sortablejs'
import { userinfo } from '@/store/index.js'
import Layer, {svLayer} from '$lib/Layer'
import Scrollbar from '$lib/Scrollbar'
// 引入组件模板
import Skin from './component/Skin.svelte'
import Setting from './component/Setting.svelte'
import AppManage from './component/AppManage.svelte'
let dockEl
$: currentTabIndex = current
let settingLayer
beforeUpdate(() => {
const curPath = $page.url.pathname
menu.map((item, index) => {
// if(item.path == curPath) {
// currentTabIndex = index
// }
if(curPath.includes(item.path)) {
currentTabIndex = index
}
})
})
onMount(() => {
// 拖拽操作
Sortable.create(dockEl, {
handle: '.sv__dock-item',
// filter: '.xxx',
animation: 200,
delay: 0,
onEnd({ newIndex, oldIndex }) {
console.log(`新索引:${newIndex} 旧索引:${oldIndex}`)
}
})
let pbox
let items = dockEl.querySelectorAll('.sv__dock-item')
items.forEach(o => {
o.addEventListener('mouseenter', () => {
pbox = svLayer.popover({
id: 'popover__' + o.id,
content: o.getAttribute('role'),
follow: '#' + o.id,
placement: 'top',
// 是否显示箭头
// arrow: false,
// 自定义样式(背景色+文字色)
layerStyle: 'background:#333;color:#fff;border-radius:2px;font-size:12px;',
time: null,
xclose: false
})
})
o.addEventListener('mouseout', () => {
if(!pbox) return
pbox.$set({open: false})
})
})
})
onDestroy(() => {
// 退出登录关闭设置弹窗
if(!$userinfo && settingLayer) {
console.log('——+——+——+——+————+——退出啦啦啦')
settingLayer.$set({open: false})
settingLayer = null
}
})
// 切换dock选项
function changeTab(index, item) {
currentTabIndex = index
dispatch('click', index)
if(item.path) {
goto(item.path)
}
}
</script>OK,基于svelte.js开发网页版聊天项目就先分享到这里。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。