cc-comment.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. <template>
  2. <view>
  3. <view class="c_total">评论 {{ tableTotal }}</view>
  4. <template v-if="dataList && dataList.length">
  5. <view class="c_comment" v-for="(item1, index1) in dataList" :key="item1.id + index1">
  6. <!-- 一级评论 -->
  7. <CommonComp :data="item1" @likeClick="() => likeClick({ item1, index1 })"
  8. @replyClick="() => replyClick({ item1, index1 })"
  9. @deleteClick="() => deleteClick({ item1, index1 })" />
  10. <view class="children_item" v-if="item1.children && item1.children.length">
  11. <!-- 二级评论 -->
  12. <CommonComp v-for="(item2, index2) in item1.childrenShow" :key="item2.id" :data="item2"
  13. :pData="item1" @likeClick="() => likeClick({ item1, index1, item2, index2 })"
  14. @replyClick="() => replyClick({ item1, index1, item2, index2 })"
  15. @deleteClick="() => deleteClick({ item1, index1, item2, index2 })" />
  16. <!-- 展开二级评论 -->
  17. <view class="expand_reply" v-if="expandTxtShow({ item1, index1 })"
  18. @tap="() => expandReplyFun({ item1, index1 })">
  19. <span class="txt"> 展开{{ item1.children.length - item1.childrenShow.length }}条回复 </span>
  20. <uni-icons type="down" size="24" color="#007aff"></uni-icons>
  21. </view>
  22. <!-- 折叠二级评论 -->
  23. <view class="shrink_reply" v-if="shrinkTxtShow({ item1, index1 })"
  24. @tap="() => shrinkReplyFun({ item1, index1 })">
  25. <span class="txt"> 收起回复内容 </span>
  26. <uni-icons type="up" size="24" color="#007aff"></uni-icons>
  27. </view>
  28. </view>
  29. </view>
  30. </template>
  31. <!-- 空盒子 -->
  32. <view class="empty_box" v-else>
  33. <!-- <uni-icons type="chatboxes" size="36" color="#c0c0c0"></uni-icons> -->
  34. <image src="@/static/icon/quexing_01.png" style="width: 380rpx; height: 308rpx;"></image>
  35. <view>
  36. <span class="txt"> 来鼓励一下作者吧~ </span>
  37. <span class="txt click" @click="() => newCommentFun()">说点什么...</span>
  38. </view>
  39. </view>
  40. <!-- 评论弹窗 -->
  41. <uni-popup ref="cPopupRef" type="bottom" @change="popChange" style="z-index: 100;">
  42. <view class="c_popup_box">
  43. <view class="reply_text">
  44. <template v-if="Object.keys(replyTemp).length">
  45. <span class="text_aid">回复给</span>
  46. <img class="user_avatar"
  47. :src="replyTemp.item2 ? replyTemp.item2.user_avatar : replyTemp.item1.user_avatar" />
  48. <span class="text_main">{{ replyTemp.item2 ? replyTemp.item2.user_name :
  49. replyTemp.item1.user_name }}</span>
  50. </template>
  51. <span v-else class="text_main">发表新评论</span>
  52. </view>
  53. <view class="content">
  54. <view class="text_area">
  55. <textarea class="textarea" v-model="commentValue" :placeholder="commentPlaceholder"
  56. :focus="focus" maxlength="300" auto-height @focus="onTextareaFocus"
  57. @keydown="handleKeydown"></textarea>
  58. <view class="emoji-trigger" @tap="toggleEmojiPanel">
  59. <text class="fa fa-smile-o"></text>
  60. </view>
  61. </view>
  62. <view class="send_btn" @tap="() => sendClick()">发送</view>
  63. </view>
  64. <!-- 表情面板 -->
  65. <view class="emoji-panel" v-if="showEmojiPanel">
  66. <view class="emoji-grid">
  67. <view class="emoji-item" v-for="(emoji, index) in emojiList" :key="index"
  68. @tap="selectEmoji(emoji)">
  69. {{ emoji }}
  70. </view>
  71. </view>
  72. </view>
  73. </view>
  74. </uni-popup>
  75. <!-- 删除弹窗 -->
  76. <!-- <uni-popup ref="delPopupRef" type="dialog">
  77. <uni-popup-dialog mode="base" title="" content="确定删除这条评论吗?" :before-close="true" @close="delCloseFun"
  78. @confirm="delConfirmFun"></uni-popup-dialog>
  79. </uni-popup> -->
  80. <DialogBox ref="DialogBox"></DialogBox>
  81. </view>
  82. </template>
  83. <script>
  84. import CommonComp from "./componets/common";
  85. import { getStorage, setStorage, removeStorage } from "@/common/util.js";
  86. import { mapState, mapMutations, mapGetters } from 'vuex';
  87. export default {
  88. components: {
  89. CommonComp
  90. },
  91. computed: {
  92. ...mapState('rightsManagement', ['teenageMode'])
  93. },
  94. props: {
  95. /** 登陆用户信息
  96. * id: number // 登陆用户id
  97. * user_name: number // 登陆用户名
  98. * user_avatar: string // 登陆用户头像地址
  99. */
  100. myInfo: {
  101. type: Object,
  102. default: () => { },
  103. },
  104. /** 文章作者信息
  105. * id: number // 文章作者id
  106. * user_name: number // 文章作者名
  107. * user_avatar: string // 文章作者头像地址
  108. */
  109. userInfo: {
  110. type: Object,
  111. default: () => { },
  112. },
  113. author: { // 文章作者信息
  114. type: Object,
  115. default: () => { },
  116. },
  117. /** 评论列表
  118. * id: number // 评论id
  119. * parent_id: number // 父级评论id
  120. * reply_id: number // 被回复人评论id
  121. * reply_name: string // 被回复人名称
  122. * user_name: string // 用户名
  123. * user_avatar: string // 评论者头像地址
  124. * user_content: string // 评论内容
  125. * is_like: boolean // 是否点赞
  126. * like_count: number // 点赞数统计
  127. * create_time: string // 创建时间
  128. */
  129. tableData: {
  130. type: Array,
  131. default: () => [],
  132. },
  133. // 评论总数
  134. tableTotal: {
  135. type: Number,
  136. default: 0,
  137. },
  138. // 评论删除模式
  139. // bind - 当被删除的一级评论存在回复评论, 那么该评论内容变更显示为[当前评论内容已被移除]
  140. // only - 仅删除当前评论(后端删除相关联的回复评论, 否则总数显示不对)
  141. // all - 删除所有评论包括回复评论
  142. deleteMode: {
  143. type: String,
  144. default: "all",
  145. },
  146. isComment: {
  147. default: true
  148. }
  149. },
  150. data() {
  151. return {
  152. dataList: [], // 渲染数据(前端的格式)
  153. replyTemp: {}, // 回复临时数据
  154. isNewComment: false, // 是否为新评论
  155. focus: false, // 评论弹窗
  156. commentValue: "", // 输入框值
  157. commentPlaceholder: "说点什么...", // 输入框占位符
  158. delTemp: {}, // 删除临时数据
  159. showEmojiPanel: false, // 是否显示表情面板
  160. emojiList: ['😀', '😃', '😄', '😁', '😆', '😅', '😂', '🤣', '🥲', '😊', '😇', '🙂', '🙃', '😉', '😌', '😍', '🥰', '😘', '😗', '😙', '😚', '😋', '😛', '😝', '😜', '🤪', '🤨', '🧐', '🤓', '😎', '🥸', '🤩', '🥳', '😏', '😒', '😞', '😔', '😟', '😕', '🙁', '☹️', '😣', '😖', '😫', '😩', '🥺', '😢', '😭', '😤', '😠', '😡', '🤬', '🤯', '😳', '🥵', '🥶', '😱', '😨', '😰', '😥', '😓', '🤗', '🤔', '🤭', '🤫', '🤥', '😶', '😐', '😑', '😬', '🙄', '😯', '😦', '😧', '😮', '😲', '🥱', '😴', '🤤', '😪', '😵', '🤐', '🥴', '🤢', '🤮', '🤧', '😷', '🤒', '🤕', '🤑', '🤠'
  161. ]
  162. };
  163. },
  164. watch: {
  165. tableData: {
  166. handler(newVal) {
  167. if (newVal.length !== this.dataList.length) {
  168. this.dataList = this.treeTransForm(newVal);
  169. }
  170. },
  171. deep: true,
  172. immediate: true,
  173. },
  174. },
  175. mounted() { },
  176. methods: {
  177. // 数据转换
  178. treeTransForm(data) {
  179. let newData = JSON.parse(JSON.stringify(data));
  180. let result = [];
  181. let map = {};
  182. newData.forEach((item, i) => {
  183. console.log(this.myInfo, this.userInfo, "---------");
  184. item.owner = item.user_id === this.myInfo.user_id; // 是否为当前登陆用户
  185. item.author = item.user_id === this.userInfo.user_id; // 是否为作者
  186. map[item.id] = item;
  187. });
  188. newData.forEach((item) => {
  189. let parent = map[item.parent_id];
  190. if (parent) {
  191. (parent.children || (parent.children = [])).push(item); // 所有回复
  192. if (parent.children.length === 1) {
  193. (parent.childrenShow = []).push(item); // 显示的回复
  194. }
  195. } else {
  196. result.push(item);
  197. }
  198. });
  199. return result;
  200. },
  201. // 点赞
  202. setLike(item) {
  203. item.is_like = !item.is_like;
  204. item.like_count = item.is_like ? item.like_count + 1 : item.like_count - 1;
  205. },
  206. likeClick({
  207. item1,
  208. index1,
  209. item2,
  210. index2
  211. }) {
  212. let item = item2 || item1;
  213. let that = this;
  214. this.setLike(item);
  215. this.$emit("likeFun", {
  216. params: item
  217. }, (res) => {
  218. // 请求后端失败, 重置点赞
  219. that.setLike(item);
  220. });
  221. },
  222. // 回复
  223. replyClick({
  224. item1,
  225. index1,
  226. item2,
  227. index2
  228. }) {
  229. uni.$emit('check_login', () => {
  230. let isContentRecommendation
  231. if (this.teenageMode != 1) {
  232. isContentRecommendation = true;
  233. } else {
  234. isContentRecommendation = false;
  235. }
  236. if (!isContentRecommendation) {
  237. uni.showToast({
  238. title: '当前无法评论',
  239. icon: 'none'
  240. });
  241. return;
  242. }
  243. if (this.isComment == 0) {
  244. uni.showToast({
  245. title: '暂无评论权限',
  246. icon: 'none'
  247. })
  248. return
  249. }
  250. this.replyTemp = JSON.parse(JSON.stringify({
  251. item1,
  252. index1,
  253. item2,
  254. index2
  255. }));
  256. this.$refs["cPopupRef"].open();
  257. })
  258. },
  259. // 发起新评论
  260. newCommentFun() {
  261. uni.$emit('check_login', () => {
  262. let isContentRecommendation
  263. if (this.teenageMode != 1) {
  264. isContentRecommendation = true;
  265. } else {
  266. isContentRecommendation = false;
  267. }
  268. if (!isContentRecommendation) {
  269. uni.showToast({
  270. title: '当前无法评论',
  271. icon: 'none'
  272. });
  273. return;
  274. }
  275. if (this.isComment == 0) {
  276. uni.showToast({
  277. title: '暂无评论权限',
  278. icon: 'none'
  279. })
  280. return
  281. }
  282. this.isNewComment = true;
  283. this.$refs["cPopupRef"].open();
  284. })
  285. },
  286. // 评论弹窗
  287. popChange(e) {
  288. uni.$emit('check_login', () => {
  289. // 关闭弹窗
  290. if (!e.show) {
  291. this.commentValue = ""; // 清空输入框值
  292. this.replyTemp = {}; // 清空被回复人信息
  293. this.isNewComment = false; // 恢复是否为新评论默认值
  294. this.showEmojiPanel = false; // 隐藏表情面板
  295. }
  296. this.focus = e.show;
  297. })
  298. },
  299. // 切换表情面板显示状态
  300. toggleEmojiPanel() {
  301. this.showEmojiPanel = !this.showEmojiPanel;
  302. },
  303. // 选择表情
  304. selectEmoji(emoji) {
  305. this.commentValue += emoji;
  306. },
  307. // 输入框获取焦点时
  308. onTextareaFocus() {
  309. // 可以选择不关闭表情面板,让用户同时使用键盘和表情
  310. // this.showEmojiPanel = false;
  311. },
  312. // 发送评论
  313. sendClick({
  314. item1,
  315. index1,
  316. item2,
  317. index2
  318. } = this.replyTemp) {
  319. uni.$emit('check_login', () => {
  320. let item = item2 || item1;
  321. let params = {};
  322. // 新评论
  323. if (this.isNewComment) {
  324. params = {
  325. id: Math.random(), // 评论id
  326. parent_id: null, // 父级评论id
  327. reply_id: null, // 被回复评论id
  328. reply_name: null, // 被回复人名称
  329. };
  330. } else {
  331. // 回复评论
  332. params = {
  333. id: Math.random(), // 评论id
  334. parent_id: item && item.parent_id ? item.parent_id : item.id, // 父级评论id
  335. reply_id: item.id, // 被回复评论id
  336. reply_name: item.user_name, // 被回复人名称
  337. };
  338. }
  339. params = {
  340. ...params,
  341. user_id: this.myInfo.user_id, // 用户id
  342. user_name: this.myInfo.user_name, // 用户名
  343. user_avatar: this.myInfo.user_avatar, // 用户头像地址
  344. user_content: this.commentValue, // 用户评论内容
  345. is_like: false, // 是否点赞
  346. like_count: 0, // 点赞数统计
  347. create_time: "刚刚", // 创建时间
  348. owner: true, // 是否为所有者 所有者可以进行删除 管理员默认true
  349. };
  350. uni.showLoading({
  351. title: "正在发送",
  352. mask: true,
  353. });
  354. this.$emit("replyFun", {
  355. params
  356. }, (res) => {
  357. uni.hideLoading();
  358. // console.log("cc-comment",res);
  359. // 拿到后端返回的id赋值, 因为删除要用到id
  360. params = {
  361. ...params,
  362. id: res.id,
  363. user_name: res.user_name,
  364. user_avatar: res.user_avatar
  365. };
  366. // 新评论
  367. if (this.isNewComment) {
  368. this.dataList.push(params);
  369. } else {
  370. // 回复
  371. let c_data = this.dataList[index1];
  372. (c_data.children || (c_data.children = [])).push(params);
  373. // 如果已展开所有回复, 那么此时插入children长度会大于childrenShow长度1, 所以就直接展开显示即可
  374. if (c_data.children.length === (c_data.childrenShow || (c_data.childrenShow = [])).length +
  375. 1) {
  376. c_data.childrenShow.push(params);
  377. }
  378. }
  379. this.$emit("update:tableTotal", this.tableTotal + 1);
  380. this.$refs["cPopupRef"].close();
  381. });
  382. })
  383. },
  384. //删除
  385. deleteClick({
  386. item1,
  387. index1,
  388. item2,
  389. index2
  390. }) {
  391. uni.$emit('check_login', () => {
  392. this.delTemp = JSON.parse(JSON.stringify({
  393. item1,
  394. index1,
  395. item2,
  396. index2
  397. }));
  398. this.$refs['DialogBox'].confirm({
  399. title: '提示',
  400. content: '确定要删除该评论及其回复吗?',
  401. DialogType: 'inquiry',
  402. btn1: '取消',
  403. btn2: '确定',
  404. animation: 0
  405. }).then(() => {
  406. this.delConfirmFun()
  407. }).catch(() => {
  408. this.delCloseFun()
  409. })
  410. })
  411. },
  412. // 关闭删除弹窗
  413. delCloseFun() {
  414. this.delTemp = {};
  415. // this.$refs["delPopupRef"].close();
  416. },
  417. // 确定删除
  418. delConfirmFun({
  419. item1,
  420. index1,
  421. item2,
  422. index2
  423. } = this.delTemp) {
  424. const deleteMode = this.deleteMode;
  425. let c_data = this.dataList[index1];
  426. uni.showLoading({
  427. title: "正在删除",
  428. mask: true,
  429. });
  430. // 删除二级评论
  431. if (index2 >= 0) {
  432. this.$emit("deleteFun", {
  433. params: [c_data.children[index2].id],
  434. mode: deleteMode
  435. }, (res) => {
  436. uni.hideLoading();
  437. this.$emit("update:tableTotal", this.tableTotal - 1);
  438. c_data.children.splice(index2, 1);
  439. c_data.childrenShow.splice(index2, 1);
  440. });
  441. } else {
  442. // 删除一级评论
  443. if (c_data?.children?.length) {
  444. // 如果一级评论包含回复评论
  445. switch (deleteMode) {
  446. case "bind":
  447. // 一级评论内容展示修改为: 当前评论内容已被移除
  448. this.$emit(
  449. "deleteFun", {
  450. params: [c_data.id],
  451. mode: deleteMode,
  452. },
  453. (res) => {
  454. uni.hideLoading();
  455. c_data.user_content = "当前评论内容已被移除";
  456. }
  457. );
  458. break;
  459. case "only":
  460. // 后端自行根据删除的一级评论id, 查找关联的子评论进行删除
  461. this.$emit(
  462. "deleteFun", {
  463. params: [c_data.id],
  464. mode: deleteMode,
  465. },
  466. (res) => {
  467. uni.hideLoading();
  468. this.$emit("update:tableTotal", this.tableTotal - c_data.children.length + 1);
  469. this.dataList.splice(index1, 1);
  470. }
  471. );
  472. break;
  473. default:
  474. // all
  475. // 收集子评论id, 提交给后端统一删除
  476. let delIdArr = [c_data.id];
  477. c_data.children.forEach((_, i) => {
  478. delIdArr.push(_.id);
  479. });
  480. this.$emit("deleteFun", {
  481. params: delIdArr,
  482. mode: deleteMode
  483. }, (res) => {
  484. uni.hideLoading();
  485. this.$emit("update:tableTotal", this.tableTotal - c_data.children.length + 1);
  486. this.dataList.splice(index1, 1);
  487. });
  488. break;
  489. }
  490. } else {
  491. // 一级评论无回复, 直接删除
  492. this.$emit("deleteFun", {
  493. params: [c_data.id],
  494. mode: deleteMode
  495. }, (res) => {
  496. uni.hideLoading();
  497. this.$emit("update:tableTotal", this.tableTotal - 1);
  498. this.dataList.splice(index1, 1);
  499. });
  500. }
  501. }
  502. this.delCloseFun();
  503. },
  504. // 展开评论if
  505. expandTxtShow({
  506. item1,
  507. index1
  508. }) {
  509. return item1.childrenShow?.length && item1.children.length - item1.childrenShow.length;
  510. },
  511. // 展开更多评论
  512. expandReplyFun({
  513. item1,
  514. index1
  515. }) {
  516. let csLen = this.dataList[index1].childrenShow.length;
  517. this.dataList[index1].childrenShow.push(
  518. ...this.dataList[index1].children.slice(csLen, csLen + 6) // 截取5条评论
  519. );
  520. },
  521. // 收起评论if
  522. shrinkTxtShow({
  523. item1,
  524. index1
  525. }) {
  526. return item1.childrenShow?.length >= 2 && item1.children.length - item1.childrenShow.length === 0;
  527. },
  528. // 收起更多评论
  529. shrinkReplyFun({
  530. item1,
  531. index1
  532. }) {
  533. this.dataList[index1].childrenShow = [];
  534. this.dataList[index1].childrenShow.push(
  535. ...this.dataList[index1].children.slice(0, 1) // 截取1条评论
  536. );
  537. },
  538. handleKeydown(event) { // 新增方法
  539. console.log(event.key, event.shiftKey);
  540. if (event.key === 'Enter' && !event.shiftKey) {
  541. event.preventDefault();
  542. this.sendClick();
  543. }
  544. },
  545. },
  546. };
  547. </script>
  548. <style lang="scss" scoped>
  549. @font-face {
  550. font-family: 'FontAwesome';
  551. src: url('https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/fonts/fontawesome-webfont.woff2') format('woff2');
  552. font-weight: normal;
  553. font-style: normal;
  554. }
  555. .fa {
  556. display: inline-block;
  557. font: normal normal normal 14px/1 FontAwesome;
  558. font-size: inherit;
  559. text-rendering: auto;
  560. -webkit-font-smoothing: antialiased;
  561. }
  562. .fa-smile-o:before {
  563. content: "\f118";
  564. }
  565. ////////////////////////
  566. .center {
  567. display: flex;
  568. align-items: center;
  569. }
  570. ////////////////////////
  571. .c_total {
  572. padding: 20rpx 30rpx 0 28rpx;
  573. font-size: 28rpx;
  574. display: none;
  575. }
  576. .empty_box {
  577. display: flex;
  578. justify-content: center;
  579. align-items: center;
  580. flex-direction: column;
  581. padding: 150rpx 10rpx;
  582. font-size: 28rpx;
  583. .txt {
  584. color: $uni-text-color-disable;
  585. }
  586. .click {
  587. color: $uni-color-primary;
  588. }
  589. }
  590. .c_comment {
  591. padding: 10rpx 30rpx;
  592. font-size: 28rpx;
  593. .children_item {
  594. padding: 6rpx 0rpx 6rpx 0rpx;
  595. margin-top: 10rpx;
  596. margin-left: 96rpx;
  597. // background-color: $uni-bg-color-grey;
  598. ::v-deep.user_avatar {
  599. width: 44rpx;
  600. height: 44rpx;
  601. }
  602. .expand_reply,
  603. .shrink_reply {
  604. margin-top: 10rpx;
  605. margin-left: 65rpx;
  606. display: inline-flex;
  607. align-items: center;
  608. .txt {
  609. font-weight: 500;
  610. color: $uni-color-primary;
  611. font-size: 26rpx !important;
  612. }
  613. .uni-icons {
  614. font-size: 30rpx !important;
  615. display: inline-block;
  616. padding-top: 2rpx;
  617. }
  618. }
  619. }
  620. }
  621. .c_popup_box {
  622. background-color: #fff;
  623. margin-bottom: 0rpx;
  624. .reply_text {
  625. @extend .center;
  626. padding: 20rpx 20rpx 0 20rpx;
  627. font-size: 26rpx;
  628. .text_aid {
  629. color: $uni-text-color-grey;
  630. margin-right: 5rpx;
  631. }
  632. .user_avatar {
  633. width: 48rpx;
  634. height: 48rpx;
  635. border-radius: 50%;
  636. margin-right: 6rpx;
  637. margin-left: 12rpx;
  638. }
  639. }
  640. .content {
  641. @extend .center;
  642. .text_area {
  643. flex: 1;
  644. padding: 20rpx;
  645. position: relative;
  646. .textarea {
  647. width: 100%;
  648. min-height: 80rpx;
  649. font-size: 28rpx;
  650. color: #333;
  651. background: #f8f8f8;
  652. border: 2rpx solid #eee;
  653. border-radius: 8rpx;
  654. padding: 16rpx 50rpx 16rpx 16rpx;
  655. }
  656. .emoji-trigger {
  657. position: absolute;
  658. right: 30rpx;
  659. bottom: 30rpx;
  660. width: 60rpx;
  661. height: 60rpx;
  662. display: flex;
  663. align-items: center;
  664. justify-content: center;
  665. color: #666;
  666. font-size: 40rpx;
  667. &:active {
  668. opacity: 0.7;
  669. }
  670. }
  671. }
  672. .send_btn {
  673. @extend .center;
  674. justify-content: center;
  675. width: 120rpx;
  676. height: 60rpx;
  677. border-radius: 20rpx;
  678. font-size: 28rpx;
  679. color: #fff;
  680. background-color: $uni-color-primary;
  681. margin-right: 20rpx;
  682. margin-left: 5rpx;
  683. }
  684. }
  685. .emoji-panel {
  686. padding: 20rpx;
  687. background-color: #f8f8f8;
  688. border-top: 2rpx solid #eee;
  689. max-height: 40vh;
  690. overflow-y: scroll;
  691. .emoji-grid {
  692. display: flex;
  693. flex-wrap: wrap;
  694. // overflow-y: scroll;
  695. .emoji-item {
  696. width: 12.5%;
  697. height: 80rpx;
  698. display: flex;
  699. align-items: center;
  700. justify-content: center;
  701. font-size: 40rpx;
  702. &:active {
  703. background-color: rgba(0, 0, 0, 0.1);
  704. border-radius: 8rpx;
  705. }
  706. }
  707. }
  708. }
  709. }
  710. </style>