chore: 重新构建git仓库

This commit is contained in:
2026-01-22 10:30:23 +08:00
parent 962560ea31
commit 7cb4f30b85
756 changed files with 114900 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
## 1.2.22022-01-24
* 去除示例项目中对uView的依赖精简示例项目代码
* 修复bug
## 1.2.12022-01-24
* 更新图标由svg变为png解决在App中图标不显示且影响页面排版的问题
## 1.2.02021-11-18
* 不再依赖uview与1.1.4相比展开样式有调整,其他完全一致
## 1.1.42021-07-22
* 降低弹出键盘的延时,显著提升流畅感
## 1.1.32021-07-22
* 修改发布输入框,从原来的放在页面最底下改为弹出式
## 1.1.22021-07-21
* 输入框聚焦前滚动到页面底部
## 1.1.12021-07-21
* 修复了微信小程序中点赞删除等操作失败的问题
## 1.1.02021-07-20
* 分离插件与接口逻辑
* 支持uni-modules更方便升级等
* 修复一些问题

View File

@@ -0,0 +1,84 @@
<template>
<view class="bottom_input_box" :style="show ? 'bottom: 430rpx' : ''">
<!-- <image src="../static/audio.png" mode=""></image> -->
<image src="./emoji.png" @click="emojichet" mode=""></image><Emoji :show="show" @emoji="emojiChange" @emojiblur="emojiblur" />
</view>
</template>
<script>
import Emoji from './emoji.vue'
export default {
data() {
return {
show: false, // 表示 emoji 列表是否显示
inputValue: '', // 输入框的值
focus: false, // 是否获得焦点
};
},
components: {
Emoji
},
methods: {
confirm() {
this.$emit('inputvalue', this.inputValue); // 发送按钮点击时把input的值传递给父组件
},
emojiblur() {
this.show = false; // 关闭 emoji 列表
},
emojiChange(e) {
this.inputValue += e; // 添加 emoji 到输入框中
},
keychange() {
this.show = false; // 关闭 emoji 列表并打开键盘
this.focus = true;
},
emojichet() {
this.show = true; // 打开 emoji 列表
}
}
};
</script>
<style scoped lang="scss">
.bottom_input_box {
position: fixed;
bottom: 0;
height: 160rpx;
width: 100%;
z-index: 99;
background: #fff;
display: flex;
align-items: center;
justify-content: space-around;
border-radius: 20rpx 20rpx 0px 0px;
image {
height: 62rpx;
width: 62rpx;
}
.input_ {
height: 62rpx;
width: calc(100% - 300rpx);
border: 1rpx solid #bbbbbb;
border-radius: 30rpx;
input {
width: 90%;
height: 100%;
margin: 0 auto;
}
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,79 @@
<template>
<view class="emoji_blur" @click="emoji_blur" v-if="show">
<view class="emoji_box">
<scroll-view class="scrool" scroll-y="true">
<view class="emojis">
<text
@click.stop="tap(item)"
v-for="(item, index) in list"
:key="index"
>
{{ item }}
</text>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
props: {
show: Boolean
},
data() {
return {
list: [
'😃', '😆', '😅', '🤣', '😂', '🙂', '🙃', '😉', '😊', '😇', '🥰', '😍', '🤩', '😘', '😗', '☺️', '😚', '😙',
'😋', '😛', '😜', '🤪', '😝', '🤑', '😩', '😖', '😨', '😭', '😞', '🤓', '🤠', '😎', '🥳', '😵', '🤧', '🤕',
'🤗', '🤭', '🤫', '🤔', '🤐', '🤨', '😐', '😑', '😶', '😏', '😒', '🙄', '😬', '🤥', '😶‍🌫️', '😮‍🌫️', '😴',
'🤮', '❤️', '💯', '💔', '💩', '🤡', '🤖', '👻', '🙈', '🙉', '🙊', '💣', '💋', '😡', '🤬', '😠'
]
};
},
methods: {
tap(item) {
this.$emit('emoji', item); // 触发 emoji 事件,将选择的 emoji 传给父组件
},
emoji_blur() {
this.$emit('emojiblur'); // 触发 emojiblur 事件,告诉父组件关闭 emoji 选择器
}
}
};
</script>
<style scoped lang="less">
.emoji_blur {
height: 100vh;
width: 100%;
position: absolute;
top: 0;
background: transparent;
}
.emoji_box {
height: 430rpx;
background: #f6f6f6;
position: fixed;
bottom: 0;
left: 0;
}
.scrool {
height: 400rpx;
width: 100%;
margin-top: 30rpx;
}
.emojis {
width: 95%;
margin: 0 auto;
font-size: 40rpx;
display: flex;
flex-wrap: wrap;
gap: 10rpx 25rpx;
flex-shrink: 3;
padding-bottom: 50rpx;
}
</style>

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

View File

@@ -0,0 +1,83 @@
{
"id": "hb-comment",
"displayName": "评论列表,回复,点赞,删除,留言板",
"version": "1.2.2",
"description": "评论列表,回复,点赞,删除,留言板",
"keywords": [
"评论列表",
"评论回复",
"评论点赞",
"评论删除",
"留言板"
],
"repository": "https://github.com/dr34-m/hb-comment",
"engines": {
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@@ -0,0 +1,261 @@
## 接口使用,包括点赞回复评论删除逻辑,建议参考示例项目进行二次封装;
## 本插件设计之初就是为小型项目设计,不支持分页,评论只有两级。如需改为多级或者需要支持分页,需要进行相当程度的改造。
直接使用
```html
<hb-comment ref="hbComment" @add="add" @del="del" @like="like" @focusOn="focusOn" :deleteTip="'确认删除?'"
:cmData="commentData" v-if="commentData"></hb-comment>
```
后端返回数据格式(给到前端后前端整合成树)
```js
{
"readNumer": 193,
"commentList": [{
"id": 1, // 唯一主键
"owner": false, // 是否是拥有者为true则可以删除管理员全部为true
"hasLike": false, // 是否点赞
"likeNum": 2, // 点赞数量
"avatarUrl": "https://inews.gtimg.com/newsapp_ls/0/13797755537/0", // 评论者头像地址
"nickName": "超长昵称超长...", // 评论者昵称,昵称过长请在后端截断
"content": "啦啦啦啦", // 评论内容
"parentId": null, // 所属评论的唯一主键
"createTime": "2021-07-02 16:32:07" // 创建时间
},
{
"id": 2,
"owner": false,
"hasLike": false,
"likeNum": 2,
"avatarUrl": "https://inews.gtimg.com/newsapp_ls/0/13797761970/0",
"nickName": "寂寞无敌",
"content": "我是评论的评论",
"parentId": 1,
"createTime": "2021-07-02 17:05:50"
},
{
"id": 4,
"owner": true,
"hasLike": true,
"likeNum": 1,
"avatarUrl": "https://inews.gtimg.com/newsapp_ls/0/13797763270/0",
"nickName": "name111",
"content": "评论啦啦啦啦啦啦啦啦啦啦",
"parentId": null,
"createTime": "2021-07-13 09:37:50"
},
{
"id": 5,
"owner": false,
"hasLike": false,
"likeNum": 0,
"avatarUrl": "https://inews.gtimg.com/newsapp_ls/0/13797755537/0",
"nickName": "超长昵称超长...",
"content": "超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论超长评论",
"parentId": null,
"createTime": "2021-07-13 16:04:35"
},
{
"id": 13,
"owner": false,
"hasLike": false,
"likeNum": 0,
"avatarUrl": "https://inews.gtimg.com/newsapp_ls/0/13797755537/0",
"nickName": "超长昵称超长...",
"content": "@寂寞无敌 你怕不是个大聪明",
"parentId": 1,
"createTime": "2021-07-14 11:01:23"
}
]
}
```
假设后端返回数据为`res`,则`commentData`可以这样得到
```js
this.commentData = {
"readNumer": res.readNumer,
"commentSize": res.commentList.length,
"comment": this.getTree(res.commentList)
}
```
其中`getTree`方法如下
```js
getTree(data) {
let result = [];
let map = {};
data.forEach(item => {
map[item.id] = item;
});
data.forEach(item => {
let parent = map[item.parentId];
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
result.push(item);
}
});
return result;
}
```
发布弹框借鉴了[@tenniswill](https://ext.dcloud.net.cn/publisher?id=74739)先生[评论内容发布组件](https://ext.dcloud.net.cn/plugin?id=1302)的思路,同时参照其评论区对代码作了优化
## 后端的Python实现核心代码可以参考
* mysql结构还有一张userlist表没有列出请自己发挥
```sql
-- ----------------------------
-- Table structure for comment
-- ----------------------------
DROP TABLE IF EXISTS `comment`;
CREATE TABLE `comment` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`article_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '所属文章id',
`comment_user_id` int(10) UNSIGNED NULL DEFAULT NULL COMMENT '评论人id',
`parent_id` int(10) UNSIGNED NULL DEFAULT NULL COMMENT '所属评论id主评论为null',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '评论内容',
`like` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '点赞(存储点赞人id数组)',
`status` tinyint(3) UNSIGNED NULL DEFAULT 0 COMMENT '状态0-未审核1-展现2-审核驳回3-已删除',
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime(0) NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(0),
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '评论' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for article_read
-- ----------------------------
DROP TABLE IF EXISTS `article_read`;
CREATE TABLE `article_read` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`article_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '所属文章id',
`read_num` int(10) UNSIGNED NULL DEFAULT 1 COMMENT '阅读数',
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime(0) NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(0),
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `unionKey`(`article_id`) USING BTREE COMMENT '文章唯一'
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '阅读数' ROW_FORMAT = Compact;
```
* python核心代码其中`CS`为项目中封装的一个通用方法,请自己发挥并替换
```python
# 提交评论
def comment_article(articleId, userId, pId, content):
if userId is None:
raise CS.CustomException("请先登录", 500)
if pId == 'null':
pId = None
conn = CS.getCoon()
cursor = conn.cursor()
cursor.execute("insert into comment(article_id,comment_user_id,parent_id,content,status,`like`) values(%s,%s,%s,%s,1,'[]')",
(articleId, userId, pId, content))
conn.commit()
cursor.close()
conn.close()
return CS.ResultMap(None)
# 点赞评论
def like_comment(commentId, userId):
if userId is None:
raise CS.CustomException("请先登录", 500)
conn = CS.getCoon()
cursor = conn.cursor()
cursor.execute("select `like` from comment where id = %s", (commentId))
currentLike_json = cursor.fetchall()[0][0]
currentLike = json.loads(currentLike_json)
if userId in currentLike:
currentLike.remove(userId)
else:
currentLike.append(userId)
currentLike_json = json.dumps(currentLike)
cursor.execute("update comment set `like` = %s where id = %s", (currentLike_json, commentId))
conn.commit()
cursor.close()
conn.close()
return CS.ResultMap(None)
# 删除评论
def delete_comment(commentId, userId):
if userId is None:
raise CS.CustomException("请先登录", 500)
conn = CS.getCoon()
cursor = conn.cursor()
# userId = 1为超级管理员
if userId != 1:
cursor.execute("select comment_user_id from comment where id = %s and status = 1",(commentId))
commentUserIdRst = cursor.fetchall()
if str(commentUserIdRst) == '()':
raise CS.CustomException("评论不存在或已删除", 500)
if userId != commentUserIdRst[0][0]:
cursor.close()
conn.close()
raise CS.CustomException("无删除权限", 500)
cursor.execute("update comment set status=3 where id = %s or parent_id = %s", (commentId, commentId))
conn.commit()
cursor.close()
conn.close()
return CS.ResultMap(None)
# 获取评论列表
def get_article_comment(articleId, userId):
conn = CS.getCoon()
cursor = conn.cursor()
cursor.execute(
'''select
c.id,
c.comment_user_id,
u.avatar_url,
u.nick_name,
c.content,
c.like,
c.parent_id,
c.create_time
from comment c
left join userlist u on c.comment_user_id = u.id
where article_id = %s and status = 1''', (articleId))
commentList = cursor.fetchall()
cursor.execute("insert into article_read(article_id) values(%s) on DUPLICATE KEY UPDATE read_num=read_num+1", (articleId))
conn.commit()
cursor.execute("select read_num from article_read where article_id = %s", (articleId))
readNum = cursor.fetchall()[0][0]
cursor.close()
conn.close()
commentResult = []
for comment in commentList:
like = json.loads(comment[5])
commentEach = {
"id": comment[0],
"owner": False,
"hasLike": False,
"likeNum": len(like),
"avatarUrl": comment[2],
"nickName": "用户" if comment[3] == None else comment[3],
"content": comment[4],
"parentId": comment[6],
"createTime": time.strftime("%Y-%m-%d %H:%M:%S", datetime.datetime.timetuple(comment[7]))
}
if len(commentEach['nickName']) > 7:
commentEach['nickName'] = commentEach['nickName'][0:6] + '...'
if userId is not None:
# userId = 1为超级管理员
if userId == comment[1] or userId == 1:
commentEach["owner"] = True
if userId in like:
commentEach["hasLike"] = True
commentResult.append(commentEach)
result = {
"readNumer": readNum,
"commentList": commentResult
}
return CS.ResultMap(result)
```