mirror of
https://github.com/LingChair/LingChair-V0.git
synced 2025-12-08 01:55:50 +08:00
Compare commits
10 Commits
v0.6.1
...
v0.7.0-rc3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71b5b5b2df | ||
|
|
9c1dc4c540 | ||
|
|
f3bf5f88b5 | ||
|
|
971b8aff85 | ||
|
|
ddfbe547e1 | ||
|
|
b4bbe08ff2 | ||
|
|
6ec0005122 | ||
|
|
541f043531 | ||
|
|
7253f0a4ec | ||
|
|
f19c3c793a |
39
.github/QA.md
vendored
Normal file
39
.github/QA.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
### Q&A
|
||||
|
||||
#### 1. 协助项目
|
||||
|
||||
1. 若修改代码,请务必添加注释或者尽可能让方法名能被人所理解
|
||||
|
||||
2. 修改 readme 相关时,请不要留废话
|
||||
|
||||
3. 其它非代码注明性注释会不定期清理
|
||||
|
||||
4. 待补充
|
||||
|
||||
#### 2. 商业用途
|
||||
|
||||
不建议,不推荐,不赞同用于商业用途, 本项目的设计初衷是为了个人和团队使用, 而非用于圈钱, 因此如果确实需要商业化, 请考虑其他项目
|
||||
|
||||
因为这个项目还在初级阶段, 还有很多地方不够完善, 甚至有很多漏洞被利用, 造成不必要的损失
|
||||
|
||||
#### 3. 提问
|
||||
|
||||
1. 禁止人身攻击性回复
|
||||
|
||||
2. 请详细说明你的问题
|
||||
|
||||
3. 漏洞、BUG 类请给出代码位置
|
||||
|
||||
#### 4. 功能请求
|
||||
|
||||
目前不考虑,因为每一个做开源的人都应该以生活,以自己的感受为本
|
||||
|
||||
就像 weishu 大佬所说的:
|
||||
|
||||
> 我发现很多搞开源的开发者都把自己弄得很累
|
||||
> 其实你把它当作是钓鱼,摩托,音响,单反那样的兴趣爱好就好多了
|
||||
> 既然是爱好,就不要搞得像打第二份工一样,开心了就玩,玩腻了就扔一边
|
||||
|
||||
另外如果你提交功能 PR,我有可能会直接 Close,我没有太多精力去维护一个用不上的功能
|
||||
|
||||
#### 5. 待补充
|
||||
BIN
.github/模块化_Babel.zip
vendored
Normal file
BIN
.github/模块化_Babel.zip
vendored
Normal file
Binary file not shown.
BIN
.github/模块化_Webpack.zip
vendored
Normal file
BIN
.github/模块化_Webpack.zip
vendored
Normal file
Binary file not shown.
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
node_modules/
|
||||
ling_chair_data/
|
||||
ling_chair_config/
|
||||
ling_chair_http/
|
||||
1
babel_lib/.gitignore
vendored
Normal file
1
babel_lib/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
3
babel_lib/readme.md
Normal file
3
babel_lib/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## Babel
|
||||
|
||||
请从本仓库根目录的 .github 文件夹内找到对应的zip文件,并把 node_modules 解压在此,方可使用
|
||||
@@ -43,3 +43,12 @@ body {
|
||||
margin-top: 60px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
[n-id=pageChatSeesion]::after {
|
||||
content: "";
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
display: block;
|
||||
height: var(--pseudo-height); /* 设置伪元素的高度 */
|
||||
z-index: -1; /* 防止遮挡实际内容 */
|
||||
}
|
||||
@@ -28,9 +28,9 @@
|
||||
<meta name="force-rendering" content="webkit" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
|
||||
<!-- 给老旧的设备提供支持 支持不了, 照样没法运行 -->
|
||||
<!-- <script src='https://polyfill.io/v3/polyfill.min.js?features=default%2Cdom4%2Ces2015%2Ces2016%2Ces2017%2Ces2018%2Ces2019%2Ces2020%2Ces2021%2Ces2022%2Ces5%2Ces6%2Ces7'></script>
|
||||
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> -->
|
||||
<!-- 给老旧的设备提供支持 支持不了, 照样没法运行 不 试一下红米2可不可以-->
|
||||
<script src='https://polyfill.io/v3/polyfill.min.js?features=default%2Cdom4%2Ces2015%2Ces2016%2Ces2017%2Ces2018%2Ces2019%2Ces2020%2Ces2021%2Ces2022%2Ces5%2Ces6%2Ces7'></script>
|
||||
<!-- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> -->
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/mdui@1.0.2/dist/css/mdui.min.css" />
|
||||
@@ -41,7 +41,8 @@
|
||||
<!-- 代替私人 fixed 并提供更好的兼容性 -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/wilddeer/stickyfill@2.1.0/dist/stickyfill.min.js"></script>
|
||||
<script src="https://unpkg.com/jquery@3.7.1/dist/jquery.min.js"></script>
|
||||
<link rel="icon" href="icon.ico" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.11/dist/clipboard.min.js"></script>
|
||||
<link rel="icon" href="res/icon.ico" />
|
||||
<title>铃之椅</title>
|
||||
</head>
|
||||
|
||||
@@ -49,23 +50,48 @@
|
||||
class="mdui-theme-primary-teal mdui-theme-accent-teal mdui-drawer-body-left mdui-appbar-with-toolbar mdui-theme-layout-auto"
|
||||
id="app">
|
||||
|
||||
<input n-id="textCopier" class="mdui-hidden" />
|
||||
<input n-id="textCopierBtn" class="mdui-hidden" />
|
||||
|
||||
<div id="lingchair-app" style="height: 100%;">
|
||||
<!-- 侧滑栏 -->
|
||||
<div class="mdui-drawer" id="main-drawer">
|
||||
<ul class="mdui-list">
|
||||
<ul class="mdui-list" mdui-collapse="{accordion: true}">
|
||||
<li class="mdui-list-item mdui-ripple">
|
||||
<div class="mdui-list-item-avatar">
|
||||
<img src="default_head.png" n-id="userHead" onerror="this.src='default_head.png'" />
|
||||
<img src="default_head.png" n-id="userHead" onerror="this.src='res/default_head.png'" />
|
||||
</div>
|
||||
<div class="mdui-list-item-content"><a n-id="helloText">早安</a>, <a n-id="userNick">Unknown</a></div>
|
||||
</li>
|
||||
<li class="mdui-list-item mdui-ripple" onclick="new mdui.Dialog(viewBinding.dialogSettings.get(0)).open()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">settings</i>
|
||||
<div class="mdui-list-item-content">设置</div>
|
||||
</li>
|
||||
<li class="mdui-list-item mdui-ripple" n-id="drawerSignOut">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">exit_to_app</i>
|
||||
<div class="mdui-list-item-content">登出</div>
|
||||
</li>
|
||||
<div class="mdui-subheader">聊天</div>
|
||||
<li class="mdui-collapse-item">
|
||||
<div class="mdui-collapse-item-header mdui-list-item mdui-ripple"><i class="mdui-list-item-icon mdui-icon material-icons">contacts</i>
|
||||
<div class="mdui-list-item-content">联系人</div><i
|
||||
class="mdui-collapse-item-arrow mdui-icon material-icons">keyboard_arrow_down</i>
|
||||
</div>
|
||||
<div class="mdui-collapse-item-body mdui-list" n-id="contactsList">
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="mdui-list mdui-hidden">
|
||||
|
||||
<li class="mdui-subheader">个人</li>
|
||||
<li class="mdui-list-item mdui-ripple" onclick="new mdui.Dialog(viewBinding.dialogMyProfile.get(0)).open()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">account_circle</i>
|
||||
<div class="mdui-list-item-content">资料</div>
|
||||
</li>
|
||||
<li class="mdui-list-item mdui-ripple" onclick="new mdui.Dialog(viewBinding.dialogMyProfile.get(0)).open()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">person_add</i>
|
||||
<div class="mdui-list-item-content">新的好友</div>
|
||||
</li>
|
||||
<li class="mdui-subheader">客户端</li>
|
||||
<li class="mdui-list-item mdui-ripple">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">settings</i>
|
||||
@@ -75,10 +101,6 @@
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">cloud_circle</i>
|
||||
<div class="mdui-list-item-content">更换服务器</div>
|
||||
</li>
|
||||
<li class="mdui-list-item mdui-ripple" n-id="drawerSignOut">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">exit_to_app</i>
|
||||
<div class="mdui-list-item-content">退出登录</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -91,10 +113,13 @@
|
||||
</a>
|
||||
<a class="mdui-typo-title" n-id="appTitle"></a>
|
||||
<div class="mdui-toolbar-spacer"></div>
|
||||
<a onclick="ContactsList.reloadList()" n-id="contactsRefresh" class="mdui-btn mdui-btn-icon mdui-ripple">
|
||||
<a onclick="refreshAll()" n-id="contactsRefresh" class="mdui-btn mdui-btn-icon mdui-ripple">
|
||||
<i class="mdui-icon material-icons">refresh</i>
|
||||
</a>
|
||||
<a class="mdui-btn mdui-btn-icon mdui-ripple" n-id="switchNotifications" mdui-tooltip="{content:'开/关通知'}">
|
||||
<a onclick="ContactsList.openAddDialog()" n-id="contactsAdd" class="mdui-btn mdui-btn-icon mdui-ripple">
|
||||
<i class="mdui-icon material-icons">add</i>
|
||||
</a>
|
||||
<a class="mdui-btn mdui-btn-icon mdui-ripple" n-id="switchNotifications">
|
||||
<i class="mdui-icon material-icons" n-id="switchNotificationsIcon">notifications_off</i>
|
||||
</a>
|
||||
<a mdui-menu="{target: '#appbar-menu'}" class="mdui-btn mdui-btn-icon mdui-ripple">
|
||||
@@ -110,38 +135,26 @@
|
||||
|
||||
<!-- Tab 栏 -->
|
||||
<div class="mdui-tab mdui-accent-theme mdui-theme-color-auto" style="position: fixed; z-index: 114;width: 100%;"
|
||||
mdui-tab>
|
||||
mdui-tab n-id="chatTab">
|
||||
<!-- 侧滑栏的 z-index 是2000, 在移动端会直接覆盖 -->
|
||||
<a href="#page-chat-list" n-id="tabChatList" class="mdui-ripple">常用</a>
|
||||
<a href="#page-contacts" n-id="tabContacts" class="mdui-ripple">通讯录</a>
|
||||
<a href="#page-chat-seesion" n-id="tabChatSeesion" class="mdui-ripple" style="text-transform: none;"></a>
|
||||
</div>
|
||||
<div id="page-chat-list" class="mdui-p-a-2 container">
|
||||
<div class="mdui-valign content" style="margin-top: 40px;">
|
||||
<span class="mdui-center">欢迎回来! (^▽^。)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="page-contacts" class="mdui-p-a-2">
|
||||
<ul class="mdui-list" style="margin-top: 30px;">
|
||||
<li class="mdui-subheader">好友</li>
|
||||
<div n-id="contactsList">
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- 滚动到底部咋这么难写... -->
|
||||
<div id="page-chat-seesion" class="mdui-p-a-2" style="display: flex;flex-direction: column;">
|
||||
<div style="display: flex;flex-direction: column;">
|
||||
<!-- 写时间居中写到吐了 这样式表不能要了 -->
|
||||
<div
|
||||
style="margin-top: 30px;overflow: auto;width: 100%;max-width: 100%;height: 100%;max-height: 100%;min-height: 0;margin-bottom: 40px;flex: 1 1 auto;display: flex;flex-direction: column;">
|
||||
style="margin-top: 50px;overflow: auto;width: 100%;max-width: 100%;height: 100%;max-height: 100%;min-height: 0;flex: 1 1 auto;display: flex;flex-direction: column;"
|
||||
n-id="chatPager">
|
||||
<div class="mdui-center" style="margin: 15px;"><a href="javascript:;" onclick="ChatMsgAdapter.loadMore()"
|
||||
class="mdui-text-color-theme">点我</a>继续加载前面的聊天记录, 或者<a href="javascript:;"
|
||||
class="mdui-text-color-theme">加载更多</a> | <a href="javascript:;"
|
||||
onclick="ChatMsgAdapter.scrollToBottom()" class="mdui-text-color-theme">回到底部</a></div>
|
||||
<div n-id="pageChatSeesion" style="flex: 1 1 auto;display: flex;flex-direction: column;"></div>
|
||||
<div n-id="pageChatSeesion" style="flex: 1 1 auto;display: flex;flex-direction: column;position: relative;">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 妈的黑化了 私人玩意这么难整 早知道 z-index 弄死它得了 浪费我时间 我就没试过这么离谱的样式表 第三方库真难写CSS 就应该先写后端的 啊啊啊啊啊啊 -->
|
||||
<!-- 不黑化了 因为 stickyfill -->
|
||||
<div class="mdui-toolbar mdui-theme-color-auto"
|
||||
style="position: sticky;max-width: 100%;margin-bottom: -30px;bottom: 0;z-index: 101;">
|
||||
style="position: sticky;max-width: 100%;margin-bottom: -30px;bottom: 0;z-index: 101;" n-id="inputToolbar">
|
||||
<ul class="mdui-menu" id="msg-input-more">
|
||||
<li class="mdui-menu-item">
|
||||
<a class="mdui-ripple">插入图片</a>
|
||||
@@ -209,28 +222,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 资料编辑对话框 -->
|
||||
<!--
|
||||
<div class="mdui-dialog" n-id="dialogMyProfile">
|
||||
<div class="mdui-dialog-title">
|
||||
资料
|
||||
</div>
|
||||
<div class="mdui-dialog-content" style="margin-left:15px;margin-right:15px;">
|
||||
<ul class="mdui-list">
|
||||
<li class="mdui-list-item mdui-ripple" mdui-dialog-close
|
||||
onclick="(async () => {viewBinding.dialogEditNickNick.val(await NickCache.getNick(localStorage.userName));new mdui.Dialog(viewBinding.dialogEditNick.get(0)).open()})()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">edit</i>
|
||||
<div class="mdui-list-item-content">修改昵称</div>
|
||||
</li>
|
||||
<li class="mdui-list-item mdui-ripple" onclick="User.uploadHeadImage()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">account_circle</i>
|
||||
<div class="mdui-list-item-content">上传头像</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple" mdui-dialog-close>关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 编辑昵称对话框 -->
|
||||
<div class="mdui-dialog" n-id="dialogEditNick">
|
||||
@@ -245,11 +250,73 @@
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple" n-id="dialogEditNickClose" mdui-dialog-close
|
||||
onclick="new mdui.Dialog(viewBinding.dialogMyProfile.get(0)).open()">关闭</button>
|
||||
onclick="new mdui.Dialog(viewBinding.dialogSettings.get(0)).open()">关闭</button>
|
||||
<button class="mdui-btn mdui-ripple"
|
||||
onclick="User.setNick(viewBinding.dialogEditNickNick.val(), () => {mdui.snackbar('已保存, 刷新页面生效');viewBinding.dialogEditNickClose.click()})">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 懂得都懂 -->
|
||||
<div class="mdui-dialog" n-id="dialogNewFriendRequest">
|
||||
<div class="mdui-dialog-title">
|
||||
新的好友请求
|
||||
</div>
|
||||
<div class="mdui-dialog-content" style="margin-left:15px;margin-right:15px;">
|
||||
<div class="mdui-textfield">
|
||||
<label class="mdui-textfield-label">昵称</label>
|
||||
<input n-id="" class="mdui-textfield-input" maxlength="30" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple" mdui-dialog-close>关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 懂得都懂 -->
|
||||
<div class="mdui-dialog" n-id="dialogNewContact">
|
||||
<div class="mdui-dialog-title">
|
||||
添加好友/群
|
||||
</div>
|
||||
<div class="mdui-dialog-content" style="margin-left:15px;margin-right:15px;">
|
||||
<div class="mdui-textfield">
|
||||
<label class="mdui-textfield-label">好友/群的ID (不是名称)</label>
|
||||
<input n-id="dialogNewContactID" class="mdui-textfield-input" maxlength="30" type="text" />
|
||||
</div>
|
||||
<select class="mdui-select" mdui-select="{position: 'top'}" n-id="dialogNewContactType">
|
||||
<option value="single">好友</option>
|
||||
<option value="group">群聊</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple" mdui-dialog-close
|
||||
onclick="ContactsList.add(viewBinding.dialogNewContactID.val(), viewBinding.dialogNewContactType.val())">确认并关闭</button>
|
||||
<button class="mdui-btn mdui-ripple" mdui-dialog-close>取消</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 懂得都懂 -->
|
||||
<div class="mdui-dialog" n-id="dialogSettings">
|
||||
<div class="mdui-dialog-title">
|
||||
设置
|
||||
</div>
|
||||
<div class="mdui-dialog-content">
|
||||
<ul class="mdui-list">
|
||||
<div class="mdui-subheader">我的资料</div>
|
||||
<li class="mdui-list-item mdui-ripple" mdui-dialog-close
|
||||
onclick="(async () => {viewBinding.dialogEditNickNick.val(await NickCache.getNick(localStorage.userName));new mdui.Dialog(viewBinding.dialogEditNick.get(0)).open()})()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">edit</i>
|
||||
<div class="mdui-list-item-content">修改昵称</div>
|
||||
</li>
|
||||
<li class="mdui-list-item mdui-ripple" onclick="User.uploadHeadImage()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">account_circle</i>
|
||||
<div class="mdui-list-item-content">上传头像</div>
|
||||
</li>
|
||||
<div class="mdui-subheader">客户端</div>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple" mdui-dialog-close>关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mdui-hidden">
|
||||
1272
build_cache/index.js
Normal file
1272
build_cache/index.js
Normal file
File diff suppressed because one or more lines are too long
@@ -12,9 +12,6 @@ body {
|
||||
.mdui-dialog {
|
||||
border-radius: 23px;
|
||||
}
|
||||
.mdui-snackbar {
|
||||
border-radius: 10px;
|
||||
}
|
||||
.mdui-menu {
|
||||
border-radius: 10px;
|
||||
}
|
||||
@@ -34,6 +31,14 @@ body {
|
||||
height: 40px;
|
||||
border-radius: 40px;
|
||||
}
|
||||
.mdui-select-open {
|
||||
border-radius: 10px;
|
||||
}
|
||||
@media not screen and (min-width: 768px) {
|
||||
.mdui-snackbar {
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 配色方案 */
|
||||
|
||||
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
BIN
client_src.zip
Normal file
BIN
client_src.zip
Normal file
Binary file not shown.
85
client_src/chat-message.css
Normal file
85
client_src/chat-message.css
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 铃之椅 - 把选择权还给用户, 让聊天权掌握在用户手中
|
||||
* Copyright 2024 满月叶
|
||||
* GitHub: https://github.com/MoonLeeeaf/LingChair-Web-Client
|
||||
* 本项目使用 Apache 2.0 协议开源
|
||||
*
|
||||
* Copyright 2024 MoonLeeeaf
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.chat-message-right {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-start;
|
||||
margin: 13px;
|
||||
}
|
||||
|
||||
.chat-message-left {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
margin: 13px;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
margin-top: 13px;
|
||||
margin-bottom: 7px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
font-size: medium;
|
||||
/* 使用了 CardView 就不需要边框了 */
|
||||
/* border: 1.3px solid; */
|
||||
padding: 15px;
|
||||
border-radius: 15px;
|
||||
/* 添加圆角样式 */
|
||||
/* 设置外边距为 7px */
|
||||
}
|
||||
|
||||
.message-content-with-nickname-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 7px;
|
||||
flex-direction: column;
|
||||
/* 垂直排列元素 */
|
||||
align-items: flex-end;
|
||||
/* 左对齐元素 */
|
||||
}
|
||||
|
||||
.message-content-with-nickname-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 7px;
|
||||
flex-direction: column;
|
||||
/* 垂直排列元素 */
|
||||
align-items: flex-start;
|
||||
/* 左对齐元素 */
|
||||
}
|
||||
|
||||
.chat-message-left .message-content-with-nickname-left .nickname,
|
||||
.chat-message-right .message-content-with-nickname-right .nickname {
|
||||
margin-right: 5px;
|
||||
font-size: medium;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.chat-message-left > .avatar,
|
||||
.chat-message-right > .avatar {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
54
client_src/index.css
Normal file
54
client_src/index.css
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 铃之椅 - 把选择权还给用户, 让聊天权掌握在用户手中
|
||||
* Copyright 2024 满月叶
|
||||
* GitHub: https://github.com/MoonLeeeaf/LingChair-Web-Client
|
||||
* 本项目使用 Apache 2.0 协议开源
|
||||
*
|
||||
* Copyright 2024 MoonLeeeaf
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
html, body {
|
||||
max-height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/* overflow: hidden; */
|
||||
/*font: initial;*/
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.menu-on-message {
|
||||
margin-top: 60px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
[n-id=pageChatSeesion]::after {
|
||||
content: "";
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
display: block;
|
||||
height: var(--pseudo-height); /* 设置伪元素的高度 */
|
||||
z-index: -1; /* 防止遮挡实际内容 */
|
||||
}
|
||||
339
client_src/index.html
Normal file
339
client_src/index.html
Normal file
@@ -0,0 +1,339 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-cmn-Hans">
|
||||
<!--
|
||||
* 铃之椅 - 把选择权还给用户, 让聊天权掌握在用户手中
|
||||
* Copyright 2024 满月叶
|
||||
* GitHub: https://github.com/MoonLeeeaf/LingChair-Web-Client
|
||||
* 本项目使用 Apache 2.0 协议开源
|
||||
*
|
||||
* Copyright 2024 MoonLeeeaf
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
-->
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="force-rendering" content="webkit" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
|
||||
<!-- 给老旧的设备提供支持 支持不了, 照样没法运行 不 试一下红米2可不可以-->
|
||||
<script src='https://polyfill.io/v3/polyfill.min.js?features=default%2Cdom4%2Ces2015%2Ces2016%2Ces2017%2Ces2018%2Ces2019%2Ces2020%2Ces2021%2Ces2022%2Ces5%2Ces6%2Ces7'></script>
|
||||
<!-- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> -->
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/mdui@1.0.2/dist/css/mdui.min.css" />
|
||||
<link rel="stylesheet" href="index.css" />
|
||||
<link rel="stylesheet" href="chat-message.css" />
|
||||
<link rel="stylesheet" href="mdui-prettier.css" />
|
||||
|
||||
<!-- 代替私人 fixed 并提供更好的兼容性 -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/wilddeer/stickyfill@2.1.0/dist/stickyfill.min.js"></script>
|
||||
<script src="https://unpkg.com/jquery@3.7.1/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.11/dist/clipboard.min.js"></script>
|
||||
<link rel="icon" href="res/icon.ico" />
|
||||
<title>铃之椅</title>
|
||||
</head>
|
||||
|
||||
<body
|
||||
class="mdui-theme-primary-teal mdui-theme-accent-teal mdui-drawer-body-left mdui-appbar-with-toolbar mdui-theme-layout-auto"
|
||||
id="app">
|
||||
|
||||
<input n-id="textCopierBtn" class="mdui-hidden" />
|
||||
|
||||
<div id="lingchair-app" style="height: 100%;">
|
||||
<!-- 侧滑栏 -->
|
||||
<div class="mdui-drawer" id="main-drawer">
|
||||
<ul class="mdui-list" mdui-collapse="{accordion: true}">
|
||||
<li class="mdui-list-item mdui-ripple">
|
||||
<div class="mdui-list-item-avatar">
|
||||
<img src="default_head.png" n-id="userHead" onerror="this.src='res/default_head.png'" />
|
||||
</div>
|
||||
<div class="mdui-list-item-content"><a n-id="helloText">早安</a>, <a n-id="userNick">Unknown</a></div>
|
||||
</li>
|
||||
<li class="mdui-list-item mdui-ripple" onclick="new mdui.Dialog(viewBinding.dialogSettings.get(0)).open()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">settings</i>
|
||||
<div class="mdui-list-item-content">设置</div>
|
||||
</li>
|
||||
<li class="mdui-list-item mdui-ripple" n-id="drawerSignOut">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">exit_to_app</i>
|
||||
<div class="mdui-list-item-content">登出</div>
|
||||
</li>
|
||||
<div class="mdui-subheader">聊天</div>
|
||||
<li class="mdui-collapse-item">
|
||||
<div class="mdui-collapse-item-header mdui-list-item mdui-ripple"><i class="mdui-list-item-icon mdui-icon material-icons">contacts</i>
|
||||
<div class="mdui-list-item-content">联系人</div><i
|
||||
class="mdui-collapse-item-arrow mdui-icon material-icons">keyboard_arrow_down</i>
|
||||
</div>
|
||||
<div class="mdui-collapse-item-body mdui-list" n-id="contactsList">
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="mdui-list mdui-hidden">
|
||||
|
||||
<li class="mdui-subheader">个人</li>
|
||||
<li class="mdui-list-item mdui-ripple" onclick="new mdui.Dialog(viewBinding.dialogMyProfile.get(0)).open()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">account_circle</i>
|
||||
<div class="mdui-list-item-content">资料</div>
|
||||
</li>
|
||||
<li class="mdui-list-item mdui-ripple" onclick="new mdui.Dialog(viewBinding.dialogMyProfile.get(0)).open()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">person_add</i>
|
||||
<div class="mdui-list-item-content">新的好友</div>
|
||||
</li>
|
||||
<li class="mdui-subheader">客户端</li>
|
||||
<li class="mdui-list-item mdui-ripple">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">settings</i>
|
||||
<div class="mdui-list-item-content">设置</div>
|
||||
</li>
|
||||
<li class="mdui-list-item mdui-ripple" n-id="drawerChangeServer">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">cloud_circle</i>
|
||||
<div class="mdui-list-item-content">更换服务器</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 应用栏 -->
|
||||
<header class="mdui-appbar mdui-appbar-fixed">
|
||||
<!-- Toolbar -->
|
||||
<div class="mdui-toolbar mdui-color-theme">
|
||||
<a mdui-drawer="{target: '#main-drawer'}" class="mdui-btn mdui-btn-icon mdui-ripple">
|
||||
<i class="mdui-icon material-icons">menu</i>
|
||||
</a>
|
||||
<a class="mdui-typo-title" n-id="appTitle"></a>
|
||||
<div class="mdui-toolbar-spacer"></div>
|
||||
<a onclick="refreshAll()" n-id="contactsRefresh" class="mdui-btn mdui-btn-icon mdui-ripple">
|
||||
<i class="mdui-icon material-icons">refresh</i>
|
||||
</a>
|
||||
<a onclick="ContactsList.openAddDialog()" n-id="contactsAdd" class="mdui-btn mdui-btn-icon mdui-ripple">
|
||||
<i class="mdui-icon material-icons">add</i>
|
||||
</a>
|
||||
<a class="mdui-btn mdui-btn-icon mdui-ripple" n-id="switchNotifications">
|
||||
<i class="mdui-icon material-icons" n-id="switchNotificationsIcon">notifications_off</i>
|
||||
</a>
|
||||
<a mdui-menu="{target: '#appbar-menu'}" class="mdui-btn mdui-btn-icon mdui-ripple">
|
||||
<i class="mdui-icon material-icons">more_vert</i>
|
||||
</a>
|
||||
<ul class="mdui-menu" id="appbar-menu">
|
||||
<li class="mdui-menu-item">
|
||||
<a class="mdui-ripple" n-id="menuAbout">关于</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Tab 栏 -->
|
||||
<div class="mdui-tab mdui-accent-theme mdui-theme-color-auto" style="position: fixed; z-index: 114;width: 100%;"
|
||||
mdui-tab n-id="chatTab">
|
||||
<!-- 侧滑栏的 z-index 是2000, 在移动端会直接覆盖 -->
|
||||
<a href="#page-chat-seesion" n-id="tabChatSeesion" class="mdui-ripple" style="text-transform: none;"></a>
|
||||
</div>
|
||||
<!-- 滚动到底部咋这么难写... -->
|
||||
<div style="display: flex;flex-direction: column;">
|
||||
<!-- 写时间居中写到吐了 这样式表不能要了 -->
|
||||
<div
|
||||
style="margin-top: 50px;overflow: auto;width: 100%;max-width: 100%;height: 100%;max-height: 100%;min-height: 0;flex: 1 1 auto;display: flex;flex-direction: column;"
|
||||
n-id="chatPager">
|
||||
<div class="mdui-center" style="margin: 15px;"><a href="javascript:;" onclick="ChatMsgAdapter.loadMore()"
|
||||
class="mdui-text-color-theme">加载更多</a> | <a href="javascript:;"
|
||||
onclick="ChatMsgAdapter.scrollToBottom()" class="mdui-text-color-theme">回到底部</a></div>
|
||||
<div n-id="pageChatSeesion" style="flex: 1 1 auto;display: flex;flex-direction: column;position: relative;">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 妈的黑化了 私人玩意这么难整 早知道 z-index 弄死它得了 浪费我时间 我就没试过这么离谱的样式表 第三方库真难写CSS 就应该先写后端的 啊啊啊啊啊啊 -->
|
||||
<!-- 不黑化了 因为 stickyfill -->
|
||||
<div class="mdui-toolbar mdui-theme-color-auto"
|
||||
style="position: sticky;max-width: 100%;margin-bottom: -30px;bottom: 0;z-index: 101;" n-id="inputToolbar">
|
||||
<ul class="mdui-menu" id="msg-input-more">
|
||||
<li class="mdui-menu-item">
|
||||
<a class="mdui-ripple">插入图片</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mdui-textfield" style="width: 100%;max-width: 100%;">
|
||||
<textarea class="mdui-textfield-input" type="text" placeholder="(。・ω・。)" n-id="inputMsg"></textarea>
|
||||
</div>
|
||||
<div class="mdui-toolbar-spacer"></div>
|
||||
<a n-id="sendMsg" class="mdui-btn mdui-btn-icon mdui-ripple">
|
||||
<i class="mdui-icon material-icons">send</i>
|
||||
</a>
|
||||
<a mdui-menu="{target: '#msg-input-more', position: 'top'}" class="mdui-btn mdui-btn-icon mdui-ripple">
|
||||
<i class="mdui-icon material-icons">more_vert</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 登录对话框 -->
|
||||
<div class="mdui-dialog" n-id="dialogSignIn">
|
||||
<div class="mdui-dialog-title">
|
||||
登录到 铃之椅
|
||||
</div>
|
||||
<div class="mdui-dialog-content" style="margin-left:15px;margin-right:15px;">
|
||||
<div class="mdui-textfield" n-id="dialogSignInServerLabel">
|
||||
<i class="mdui-icon material-icons">cloud_circle</i>
|
||||
<label class="mdui-textfield-label">服务器地址</label>
|
||||
<input n-id="dialogSignInServer" class="mdui-textfield-input" type="text" placeholder="留空以使用网页所在地址"
|
||||
n-input-ls="server" n-id="dialogSignServer" onblur="setUpClient(viewBinding.dialogSignServer.val())" />
|
||||
</div>
|
||||
<div class="mdui-textfield">
|
||||
<i class="mdui-icon material-icons">account_circle</i>
|
||||
<label class="mdui-textfield-label">账号</label>
|
||||
<input n-id="dialogSignInName" class="mdui-textfield-input" maxlength="25" type="text"
|
||||
n-input-ls="userName" />
|
||||
</div>
|
||||
<div class="mdui-textfield">
|
||||
<i class="mdui-icon material-icons">lock</i>
|
||||
<label class="mdui-textfield-label">密码</label>
|
||||
<input n-id="dialogSignInPasswd" class="mdui-textfield-input" maxlength="30" type="password" />
|
||||
</div>
|
||||
<span>注:使用非已知的服务提供商提供的服务器时, 请注意个人信息保护哦 o(。・ω・。)o</span>
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple"
|
||||
onclick="User.signUp(viewBinding.dialogSignInName.val(), viewBinding.dialogSignInPasswd.val(), () => mdui.snackbar('注册成功, 请直接点击登录即可~'))">注册</button>
|
||||
<button class="mdui-btn mdui-ripple" n-id="dialogSignInEnter"
|
||||
onclick="User.signInWithDialog(viewBinding.dialogSignInName.val(), viewBinding.dialogSignInPasswd.val())">登录</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 资料卡对话框 -->
|
||||
<div class="mdui-dialog" n-id="dialogProfile">
|
||||
<div class="mdui-dialog-content" style="margin-left: 15px; margin-right: 15px; height: 101px;">
|
||||
<div style="display: flex;justify-content: flex-start">
|
||||
<img style="width: 60px; height: 60px;" class="mdui-img-circle" n-id="dialogProfileHead">
|
||||
<div n-id="dialogProfileNick" style="font-size: 22px;align-self: center;margin-left: 20px;"
|
||||
class="mdui-text-color-white"></div>
|
||||
</div>
|
||||
<div style="margin-left: 80px;"></div>
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple" mdui-dialog-close>关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="mdui-dialog" n-id="dialogMyProfile">
|
||||
<div class="mdui-dialog-title">
|
||||
资料
|
||||
</div>
|
||||
<div class="mdui-dialog-content" style="margin-left:15px;margin-right:15px;">
|
||||
<ul class="mdui-list">
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple" mdui-dialog-close>关闭</button>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 编辑昵称对话框 -->
|
||||
<div class="mdui-dialog" n-id="dialogEditNick">
|
||||
<div class="mdui-dialog-title">
|
||||
修改昵称
|
||||
</div>
|
||||
<div class="mdui-dialog-content" style="margin-left:15px;margin-right:15px;">
|
||||
<div class="mdui-textfield">
|
||||
<label class="mdui-textfield-label">昵称</label>
|
||||
<input n-id="dialogEditNickNick" class="mdui-textfield-input" maxlength="30" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple" n-id="dialogEditNickClose" mdui-dialog-close
|
||||
onclick="new mdui.Dialog(viewBinding.dialogSettings.get(0)).open()">关闭</button>
|
||||
<button class="mdui-btn mdui-ripple"
|
||||
onclick="User.setNick(viewBinding.dialogEditNickNick.val(), () => {mdui.snackbar('已保存, 刷新页面生效');viewBinding.dialogEditNickClose.click()})">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 懂得都懂 -->
|
||||
<div class="mdui-dialog" n-id="dialogNewFriendRequest">
|
||||
<div class="mdui-dialog-title">
|
||||
新的好友请求
|
||||
</div>
|
||||
<div class="mdui-dialog-content" style="margin-left:15px;margin-right:15px;">
|
||||
<div class="mdui-textfield">
|
||||
<label class="mdui-textfield-label">昵称</label>
|
||||
<input n-id="" class="mdui-textfield-input" maxlength="30" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple" mdui-dialog-close>关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 懂得都懂 -->
|
||||
<div class="mdui-dialog" n-id="dialogNewContact">
|
||||
<div class="mdui-dialog-title">
|
||||
添加好友/群
|
||||
</div>
|
||||
<div class="mdui-dialog-content" style="margin-left:15px;margin-right:15px;">
|
||||
<div class="mdui-textfield">
|
||||
<label class="mdui-textfield-label">好友/群的ID (不是名称)</label>
|
||||
<input n-id="dialogNewContactID" class="mdui-textfield-input" maxlength="30" type="text" />
|
||||
</div>
|
||||
<select class="mdui-select" mdui-select="{position: 'top'}" n-id="dialogNewContactType">
|
||||
<option value="single">好友</option>
|
||||
<option value="group">群聊</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple" mdui-dialog-close
|
||||
onclick="ContactsList.add(viewBinding.dialogNewContactID.val(), viewBinding.dialogNewContactType.val())">确认并关闭</button>
|
||||
<button class="mdui-btn mdui-ripple" mdui-dialog-close>取消</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 懂得都懂 -->
|
||||
<div class="mdui-dialog" n-id="dialogSettings">
|
||||
<div class="mdui-dialog-title">
|
||||
设置
|
||||
</div>
|
||||
<div class="mdui-dialog-content">
|
||||
<ul class="mdui-list">
|
||||
<div class="mdui-subheader">我的资料</div>
|
||||
<li class="mdui-list-item mdui-ripple" mdui-dialog-close
|
||||
onclick="(async () => {viewBinding.dialogEditNickNick.val(await NickCache.getNick(localStorage.userName));new mdui.Dialog(viewBinding.dialogEditNick.get(0)).open()})()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">edit</i>
|
||||
<div class="mdui-list-item-content">修改昵称</div>
|
||||
</li>
|
||||
<li class="mdui-list-item mdui-ripple" onclick="User.uploadHeadImage()">
|
||||
<i class="mdui-list-item-icon mdui-icon material-icons">account_circle</i>
|
||||
<div class="mdui-list-item-content">上传头像</div>
|
||||
</li>
|
||||
<div class="mdui-subheader">客户端</div>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mdui-dialog-actions">
|
||||
<button class="mdui-btn mdui-ripple" mdui-dialog-close>关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mdui-hidden">
|
||||
<input type="file" n-id="uploadHeadImage" name="选择头像" onchange="User.uploadHeadImageCallback(this)"
|
||||
accept="image/png, image/jpeg" />
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="https://unpkg.com/crypto-js@4.2.0/crypto-js.js"></script>
|
||||
<script src="https://unpkg.com/socket.io-client@4.7.4/dist/socket.io.min.js"></script>
|
||||
<script src="https://unpkg.com/mdui@1.0.2/dist/js/mdui.min.js"></script>
|
||||
<!-- 加了babel也许能解决下浏览器兼容问题 -->
|
||||
<!-- <script type="module" src="index.js"></script>
|
||||
<script nomodule type="text/babel" src="index.js"></script> -->
|
||||
|
||||
<!-- 就算弄语法兼容其他老旧设备照样用不了, 比如我的 iPad4 -->
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -21,6 +21,11 @@
|
||||
|
||||
const UrlArgs = new URL(location.href).searchParams
|
||||
|
||||
// https://www.ruanyifeng.com/blog/2021/09/detecting-mobile-browser.html
|
||||
function isMobile() {
|
||||
return ('ontouchstart' in document.documentElement);
|
||||
}
|
||||
|
||||
function setOnRightClick(e, cb) {
|
||||
if (!(e instanceof jQuery))
|
||||
e = $(e)
|
||||
@@ -112,7 +117,7 @@ class NData {
|
||||
let viewBinding = NData.mount($("#app").get(0))
|
||||
|
||||
$.ajax({
|
||||
url: "config.json",
|
||||
url: "res/config.json",
|
||||
dataType: "json",
|
||||
success: (c) => {
|
||||
viewBinding.appTitle.text(c.appTitle)
|
||||
@@ -123,22 +128,26 @@ $.ajax({
|
||||
},
|
||||
})
|
||||
|
||||
// Toolbar 快捷按钮绑定
|
||||
/* // Toolbar 快捷按钮绑定
|
||||
viewBinding.contactsRefresh.hide()
|
||||
viewBinding.contactsAdd.hide()
|
||||
viewBinding.tabChatList.on("show.mdui.tab", () => {
|
||||
viewBinding.contactsRefresh.hide()
|
||||
viewBinding.contactsAdd.hide()
|
||||
})
|
||||
viewBinding.tabContacts.on("show.mdui.tab", () => {
|
||||
viewBinding.contactsRefresh.show()
|
||||
viewBinding.contactsAdd.show()
|
||||
})
|
||||
viewBinding.tabChatSeesion.on("show.mdui.tab", () => {
|
||||
viewBinding.contactsRefresh.hide()
|
||||
})
|
||||
viewBinding.contactsAdd.hide()
|
||||
}) */
|
||||
|
||||
viewBinding.tabChatSeesion.hide()
|
||||
/* viewBinding.tabChatSeesion.hide() */
|
||||
|
||||
// 关于页面
|
||||
viewBinding.menuAbout.click(() => mdui.alert('GitHub: MoonLeeeaf<br/><br/>欢迎各位大佬访问我们的<a class="mdui-text-color-theme-accent" href="https://github.com/LingChair/LingChair">项目主页</a>', '关于 铃之椅', () => { }, { confirmText: "关闭" }))
|
||||
viewBinding.menuAbout.click(() => mdui.alert('这是一个开源项目<br/>作者: MoonLeeeaf<br/>欢迎访问我们的<a class="mdui-text-color-theme-accent" href="https://github.com/LingChair/LingChair">项目主页</a>', '关于 铃之椅', () => { }, { confirmText: "关闭" }))
|
||||
|
||||
viewBinding.drawerChangeServer.click(() => {
|
||||
mdui.prompt('输入服务器地址...(为空则使用当前页面地址)', (value) => {
|
||||
@@ -189,11 +198,12 @@ if (localStorage.useNotifications == "true")
|
||||
|
||||
// https://www.runoob.com/w3cnote/javascript-copy-clipboard.html
|
||||
function copyText(t) {
|
||||
let cp = viewBinding.textCopier.get(0)
|
||||
cp.value = t
|
||||
cp.select()
|
||||
cp.setSelectionRange(0, 99999)
|
||||
navigator.clipboard.writeText(cp.value)
|
||||
let btn = viewBinding.textCopierBtn
|
||||
btn.attr("data-clipboard-text", t)
|
||||
new ClipboardJS(btn.get(0)).on('success', (e) => {
|
||||
e.clearSelection()
|
||||
})
|
||||
btn.click()
|
||||
}
|
||||
|
||||
// https://zhuanlan.zhihu.com/p/162910462
|
||||
@@ -310,7 +320,7 @@ class ContactsList {
|
||||
/*client.emit("user.getNick", { name: localStorage.userName }, (re) => {
|
||||
let nick = re.data == null ? re.data.nick : null
|
||||
let name = ls[index]*/
|
||||
$($.parseHTML(`<li class="mdui-list-item mdui-ripple"><div class="mdui-list-item-avatar"><img src="` + User.getUserHeadUrl(name) + `" onerror="this.src='default_head.png'" /></div><div class="mdui-list-item-content">` + dick + `</div></li>`)).appendTo(viewBinding.contactsList).click(() => {
|
||||
$($.parseHTML(`<li class="mdui-list-item mdui-ripple"><div class="mdui-list-item-avatar"><img src="` + User.getUserHeadUrl(name) + `" onerror="this.src='res/default_head.png'" /></div><div class="mdui-list-item-content">` + dick + `</div></li>`)).appendTo(viewBinding.contactsList).click(() => {
|
||||
ChatMsgAdapter.switchTo(name, "single")
|
||||
})
|
||||
//})
|
||||
@@ -318,10 +328,32 @@ class ContactsList {
|
||||
|
||||
})
|
||||
}
|
||||
// 添加联系人,好友或者群聊
|
||||
static add(name, type) {
|
||||
if (type == "single") {
|
||||
|
||||
}
|
||||
}
|
||||
static openAddDialog() {
|
||||
new mdui.Dialog(viewBinding.dialogNewContact.get(0)).open()
|
||||
}
|
||||
}
|
||||
|
||||
// 第一次写前端的消息加载, 代码很乱, 还请原谅~
|
||||
|
||||
// v0.7.0 大改UI 畏惧了 太庞大了
|
||||
|
||||
class ChatPage {
|
||||
static cached = {}
|
||||
constructor(name, type) {
|
||||
|
||||
}
|
||||
static switchTo(name, type) {
|
||||
if (!this.cached[name])
|
||||
this.cached[name] = new ChatPage(name, type)
|
||||
}
|
||||
}
|
||||
|
||||
class ChatMsgAdapter {
|
||||
static type
|
||||
static target
|
||||
@@ -329,6 +361,7 @@ class ChatMsgAdapter {
|
||||
static minMsgId
|
||||
static time
|
||||
static bbn
|
||||
static resizeDick
|
||||
// 切换聊天对象
|
||||
static async switchTo(name, type) {
|
||||
viewBinding.tabChatSeesion.show()
|
||||
@@ -407,8 +440,10 @@ class ChatMsgAdapter {
|
||||
static addSystemMsg(m, re) {
|
||||
let e
|
||||
if (re)
|
||||
// 加到头部
|
||||
e = $($.parseHTML(m)).prependTo(viewBinding.pageChatSeesion)
|
||||
else
|
||||
// 加到尾部
|
||||
e = $($.parseHTML(m)).appendTo(viewBinding.pageChatSeesion)
|
||||
return e
|
||||
}
|
||||
@@ -416,7 +451,8 @@ class ChatMsgAdapter {
|
||||
let elementRect = viewBinding.pageChatSeesion.get(0).getBoundingClientRect()
|
||||
return (elementRect.bottom <= window.innerHeight)
|
||||
}
|
||||
// 不会压栈 只添加消息 返回消息的JQ对象
|
||||
// 添加消息 返回消息的JQ对象
|
||||
// name: 用户id m: 消息 t: 时间戳 re: 默认加到尾部 msgid: 消息id
|
||||
static async addMsg(name, m, t, re, msgid) {
|
||||
|
||||
let nick = await NickCache.getNick(name) // re.data == null ? name : re.data.nick
|
||||
@@ -432,11 +468,11 @@ class ChatMsgAdapter {
|
||||
<span id="msg-content">` + msg + `</span>
|
||||
</div>
|
||||
</div>
|
||||
<img class="avatar" src="` + User.getUserHeadUrl(name) + `" onerror="this.src='default_head.png'" />
|
||||
<img class="avatar" src="` + User.getUserHeadUrl(name) + `" onerror="this.src='res/default_head.png'" />
|
||||
</div>`
|
||||
else
|
||||
temp = `<div class="chat-message-left">
|
||||
<img class="avatar" src="` + User.getUserHeadUrl(name) + `" onerror="this.src='default_head.png'" />
|
||||
<img class="avatar" src="` + User.getUserHeadUrl(name) + `" onerror="this.src='res/default_head.png'" />
|
||||
<div class="message-content-with-nickname-left">
|
||||
<span class="nickname">` + nick + `</span>
|
||||
<div class="message-content mdui-card" id="msgid_` + msgid + `">
|
||||
@@ -488,10 +524,10 @@ class ChatMsgAdapter {
|
||||
} */
|
||||
static scrollToBottom() {
|
||||
// 吐了啊 原来这样就行了 我何必在子element去整啊
|
||||
window.scrollBy({
|
||||
viewBinding.chatPager.get(0).scrollBy({
|
||||
top: 1145141919810,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
// 从本地加载
|
||||
/*static loadMsgsFromLocal(target) {
|
||||
@@ -505,6 +541,26 @@ class ChatMsgAdapter {
|
||||
static saveToLocal() {
|
||||
localStorage["chat_msg_" + this.target] = JSON.stringify(this.msgList)
|
||||
}*/
|
||||
// 自动调整使输入框置底 CSS真tm靠不住啊
|
||||
static initInputResizer() {
|
||||
// 实验表面移动端切出输入法时会触发1-2次resize事件
|
||||
// 可以利用这个特性来实现自动滚动文本
|
||||
let resize = () => {
|
||||
viewBinding.pageChatSeesion.height(window.innerHeight - viewBinding.inputToolbar.height() - $("header.mdui-appbar").height() - viewBinding.chatTab.height() - 50)
|
||||
let ledi = this.resizeDick - window.innerHeight
|
||||
if (isMobile()) viewBinding.chatPager.get(0).scrollBy({
|
||||
// 5.19晚10:56分调配出来的秘方
|
||||
// < 0 为窗口变大
|
||||
// cnm的,调试十万次就你tm检测不到底是吧,就你语法天天错误是吧
|
||||
// 欺负我现在用不了电脑
|
||||
top: -(ledi) * ( (ledi < 0 && this.isAtBottom()) ? 6 : -1 ), // (ledi < 0 ? 6 : 6),
|
||||
behavior: 'smooth'
|
||||
})
|
||||
this.resizeDick = window.innerHeight
|
||||
}
|
||||
window.addEventListener("resize", resize)
|
||||
resize()
|
||||
}
|
||||
// 为消息设置长按/右键事件
|
||||
static initMsgElementEvents() {
|
||||
let listeners = {}
|
||||
@@ -716,7 +772,7 @@ else
|
||||
// 登录到账号
|
||||
let dialogSignIn
|
||||
// 谨防 localStorage 字符串数据大坑
|
||||
if (localStorage.isSignIn === "false")
|
||||
if (localStorage.isSignIn == "false")
|
||||
dialogSignIn = new mdui.Dialog(viewBinding.dialogSignIn.get(0), {
|
||||
modal: true,
|
||||
closeOnEsc: false,
|
||||
@@ -744,3 +800,11 @@ else {
|
||||
Stickyfill.add($("*").filter((a, b) => $(b).css('position') === 'sticky'))
|
||||
|
||||
ChatMsgAdapter.initMsgElementEvents()
|
||||
|
||||
ChatMsgAdapter.initInputResizer()
|
||||
|
||||
function refreshAll() {
|
||||
ContactsList.reloadList()
|
||||
delete NickCache.data
|
||||
NickCache.data = {}
|
||||
}
|
||||
53
client_src/mdui-prettier.css
Normal file
53
client_src/mdui-prettier.css
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* ©2024 满月叶
|
||||
* GitHub: MoonLeeeaf
|
||||
* 是 UI 美化,好耶!
|
||||
*/
|
||||
|
||||
/* 美化UI */
|
||||
|
||||
body {
|
||||
font-family: -apple-system, system-ui, -webkit-system-font;
|
||||
}
|
||||
.mdui-dialog {
|
||||
border-radius: 23px;
|
||||
}
|
||||
.mdui-menu {
|
||||
border-radius: 10px;
|
||||
}
|
||||
.mdui-menu-item > a {
|
||||
padding-right: 3px;
|
||||
}
|
||||
.mdui-btn:not(.mdui-btn-icon, .mdui-dialog-actions button, .mdui-dialog-actions a) {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.mdui-dialog-actions a,
|
||||
.mdui-dialog-actions button {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
height: 40px;
|
||||
border-radius: 40px;
|
||||
}
|
||||
.mdui-select-open {
|
||||
border-radius: 10px;
|
||||
}
|
||||
@media not screen and (min-width: 768px) {
|
||||
.mdui-snackbar {
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 配色方案 */
|
||||
|
||||
.mdui-theme-color-auto {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.mdui-theme-color-auto {
|
||||
background-color: #303030;
|
||||
}
|
||||
}
|
||||
4
client_src/res/config.json
Normal file
4
client_src/res/config.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"appTitle": "",
|
||||
"canChangeServer": true
|
||||
}
|
||||
BIN
client_src/res/default_head.png
Normal file
BIN
client_src/res/default_head.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
BIN
client_src/res/icon.ico
Normal file
BIN
client_src/res/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
13
client_src/res/license.txt
Normal file
13
client_src/res/license.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright 2024 MoonLeeeaf
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
16
readme.md
16
readme.md
@@ -3,19 +3,21 @@
|
||||
欢迎来到铃之椅! 这是一个即时通讯项目, 为通讯提供更多的选择, 为人民服务
|
||||
|
||||
> [!NOTE]
|
||||
> 本项目仍在实验阶段, 为防止源代码丢失, 先留存代码...
|
||||
> 本项目仍在实验阶段, [点我](final.md)可查看进展
|
||||
>
|
||||
> 如果发现有任何疑问, 欢迎提问
|
||||
> 如果有任何问题,欢迎你提出来,我会不定时查看
|
||||
>
|
||||
> 另外 Android 客户端也在开发, 但因为从头开始写且生地中考临近, 因此暂时搁置
|
||||
> 另外 Android 客户端也在开发, 但进展缓慢
|
||||
|
||||
### 使用
|
||||
|
||||
服务端:
|
||||
|
||||
1. 克隆本仓库源代码到本地
|
||||
0. 确保安装了 Node.js
|
||||
|
||||
2. 运行 run.sh 或 run.bat
|
||||
1. 克隆本仓库源代码到本地,并运行 run_build.sh 构建网页
|
||||
|
||||
2. 运行 run.sh
|
||||
|
||||
网页端:
|
||||
|
||||
@@ -23,7 +25,9 @@
|
||||
|
||||
* 克隆本仓库到本地并运行本地 HTTP 服务端
|
||||
|
||||
* 使用本仓库提供的网页 (不推荐, 有跨域问题)
|
||||
* 静态网页 (不推荐)
|
||||
|
||||
### [Q&A](.github/QA.md)
|
||||
|
||||
### 鸣谢
|
||||
|
||||
|
||||
11
run_build.sh
Normal file
11
run_build.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
# 复制资源文件,这些文件不需要编译
|
||||
mkdir -p ling_chair_http
|
||||
cp -r client_src/* build_cache/
|
||||
|
||||
# 向前兼容脚本
|
||||
cd babel_lib
|
||||
node node_modules/@babel/cli/bin/babel ../client_src --out-dir ../build_cache --presets=@babel/env
|
||||
|
||||
# 打包以减小负载
|
||||
cd ../webpack_lib
|
||||
node node_modules/webpack-cli/bin/cli.js --config webpack_config.js
|
||||
@@ -148,6 +148,21 @@ let api = {
|
||||
cb({ msg: msg, code: 0, data: { friends: friends } })
|
||||
},
|
||||
|
||||
// 添加好友
|
||||
// {name: 账号, accessToken: 访问令牌} 返回 {friends: []}
|
||||
// WIP
|
||||
"user.addFriend": (a, cb) => {
|
||||
if (checkEmpty([a.name, a.accessToken]))
|
||||
return cb({ msg: "参数缺失", code: -1 })
|
||||
|
||||
let { msg, code, friends } = users.getFriends(a.name, a.accessToken)
|
||||
|
||||
if (code !== 0)
|
||||
return cb({ msg: msg, code: code })
|
||||
|
||||
cb({ msg: msg, code: 0, data: { friends: friends } })
|
||||
},
|
||||
|
||||
"user.getNick": (a, cb) => {
|
||||
if (checkEmpty([a.name]))
|
||||
return cb({ msg: "参数缺失", code: -1 })
|
||||
|
||||
1
webpack_lib/.gitignore
vendored
Normal file
1
webpack_lib/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
3
webpack_lib/readme.md
Normal file
3
webpack_lib/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## Webpack
|
||||
|
||||
请从本仓库根目录的 .github 文件夹内找到对应的zip文件,并把 node_modules 解压在此,方可使用
|
||||
20
webpack_lib/webpack_config.js
Normal file
20
webpack_lib/webpack_config.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const path = require('path')
|
||||
const { BannerPlugin } = require('webpack')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
|
||||
module.exports = {
|
||||
entry: '../build_cache/index.js',
|
||||
output: {
|
||||
filename: 'index.js',
|
||||
path: path.resolve(__dirname, '../ling_chair_http'),
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user