MyComment.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. <template>
  2. <div :class="{'comment-active': commentActive}" style="border: 1px solid #eee; margin: 0; position: relative" @click="handleClickBlank">
  3. <textarea ref="commentRef" v-model="myComment" @input="handleCommentChange" @blur="handleBlur" class="comment-content" :rows="3" placeholder="请输入你的评论,可以@成员" />
  4. <div class="comment-content comment-html-shower" :class="{'no-content':noConent, 'top-div': showHtml, 'bottom-div': showHtml == false }" v-html="commentHtml" @click="handleClickHtmlShower"></div>
  5. <div class="comment-buttons" v-if="commentActive">
  6. <div style="cursor: pointer">
  7. <Tooltip title="选择@用户">
  8. <user-add-outlined @click="openSelectUser" />
  9. </Tooltip>
  10. <Tooltip title="上传附件">
  11. <PaperClipOutlined @click="uploadVisible = !uploadVisible" />
  12. </Tooltip>
  13. <span title="表情" style="display: inline-block">
  14. <SmileOutlined ref="emojiButton" @click="handleShowEmoji" />
  15. <div style="position: relative" v-show=""> </div>
  16. </span>
  17. </div>
  18. <div v-if="commentActive">
  19. <a-button v-if="inner" @click="noComment" style="margin-right: 10px">取消</a-button>
  20. <a-button type="primary" @click="sendComment" :loading="buttonLoading" :disabled="disabledButton">发 送</a-button>
  21. </div>
  22. </div>
  23. <upload-chunk ref="uploadRef" :visible="uploadVisible" @select="selectFirstFile"></upload-chunk>
  24. </div>
  25. <UserSelectModal labelKey="realname" rowKey="username" @register="registerModal" @getSelectResult="setValue" isRadioSelection></UserSelectModal>
  26. <a-modal v-model:visible="visibleEmoji" :footer="null" wrapClassName="emoji-modal" :closable="false" :width="490">
  27. <template #title>
  28. <span></span>
  29. </template>
  30. <Picker
  31. :pickerStyles="pickerStyles"
  32. :i18n="optionsName"
  33. :data="emojiIndex"
  34. emoji="grinning"
  35. :showPreview="false"
  36. :infiniteScroll="false"
  37. :showSearch="false"
  38. :showSkinTones="false"
  39. set="apple"
  40. @select="showEmoji">
  41. </Picker>
  42. </a-modal>
  43. </template>
  44. <script lang="ts">
  45. import {ref, watch, computed, inject} from 'vue';
  46. import { propTypes } from '/@/utils/propTypes';
  47. import { UserAddOutlined, PaperClipOutlined, SmileOutlined } from '@ant-design/icons-vue';
  48. import { Tooltip } from 'ant-design-vue';
  49. import UserSelectModal from '/@/components/Form/src/jeecg/components/modal/UserSelectModal.vue';
  50. import { useModal } from '/@/components/Modal';
  51. import UploadChunk from './UploadChunk.vue';
  52. import 'emoji-mart-vue-fast/css/emoji-mart.css';
  53. import { useEmojiHtml } from './useComment';
  54. const optionsName = {
  55. categories: {
  56. recent: '最常用的',
  57. smileys: '表情选择',
  58. people: '人物&身体',
  59. nature: '动物&自然',
  60. foods: '食物&饮料',
  61. activity: '活动',
  62. places: '旅行&地点',
  63. objects: '物品',
  64. symbols: '符号',
  65. flags: '旗帜',
  66. },
  67. };
  68. export default {
  69. name: 'MyComment',
  70. components: {
  71. UserAddOutlined,
  72. Tooltip,
  73. UserSelectModal,
  74. PaperClipOutlined,
  75. UploadChunk,
  76. SmileOutlined,
  77. },
  78. props: {
  79. inner: propTypes.bool.def(false),
  80. inputFocus: {
  81. type: Boolean,
  82. default: false,
  83. },
  84. },
  85. emits: ['cancel', 'comment'],
  86. setup(props, { emit }) {
  87. const uploadVisible = ref(false);
  88. const uploadRef = ref();
  89. //注册model
  90. const [registerModal, { openModal }] = useModal();
  91. const buttonLoading = ref(false);
  92. const myComment = ref<string>('');
  93. function sendComment() {
  94. console.log(myComment.value);
  95. let content = myComment.value;
  96. if (!content && content !== '0') {
  97. disabledButton.value = true;
  98. } else {
  99. buttonLoading.value = true;
  100. let fileList = [];
  101. if (uploadVisible.value == true) {
  102. fileList = uploadRef.value.getUploadFileList();
  103. }
  104. emit('comment', content, fileList);
  105. setTimeout(() => {
  106. buttonLoading.value = false;
  107. }, 350);
  108. }
  109. }
  110. const disabledButton = ref(false);
  111. watch(myComment, () => {
  112. let content = myComment.value;
  113. if (!content && content !== '0') {
  114. disabledButton.value = true;
  115. } else {
  116. disabledButton.value = false;
  117. }
  118. });
  119. function noComment() {
  120. emit('cancel');
  121. }
  122. const commentRef = ref();
  123. watch(
  124. () => props.inputFocus,
  125. (val) => {
  126. if (val == true) {
  127. // commentRef.value.focus()
  128. myComment.value = '';
  129. if (uploadVisible.value == true) {
  130. uploadRef.value.clear();
  131. uploadVisible.value = false;
  132. }
  133. }
  134. },
  135. { deep: true, immediate: true }
  136. );
  137. function openSelectUser() {
  138. openModal(true, {
  139. isUpdate: false,
  140. });
  141. }
  142. function setValue(options) {
  143. console.log('setValue', options);
  144. if (options && options.length > 0) {
  145. const { label, value } = options[0];
  146. if (label && value) {
  147. let str = `${label}[${value}]`;
  148. let temp = myComment.value;
  149. if (!temp) {
  150. myComment.value = '@' + str;
  151. } else {
  152. if (temp.endsWith('@')) {
  153. myComment.value = temp + str;
  154. } else {
  155. myComment.value = '@' + str + ' ' + temp;
  156. }
  157. }
  158. }
  159. }
  160. }
  161. function handleCommentChange() {
  162. //console.log(1,e)
  163. }
  164. watch(
  165. () => myComment.value,
  166. (val) => {
  167. if (val && val.endsWith('@')) {
  168. openSelectUser();
  169. }
  170. }
  171. );
  172. const emojiButton = ref();
  173. function onSelectEmoji(emoji) {
  174. let temp = myComment.value || '';
  175. temp += emoji;
  176. myComment.value = temp;
  177. emojiButton.value.click();
  178. }
  179. const visibleEmoji = ref(false);
  180. function showEmoji(e) {
  181. let temp = myComment.value || '';
  182. let str = e.colons;
  183. if (str.indexOf('::') > 0) {
  184. str = str.substring(0, str.indexOf(':') + 1);
  185. }
  186. myComment.value = temp + str;
  187. visibleEmoji.value = false;
  188. handleBlur();
  189. }
  190. const pickerStyles = {
  191. width: '490px'
  192. /* height: '350px',
  193. top: '0px',
  194. left: '-75px',
  195. position: 'absolute',
  196. 'z-index': 9999*/
  197. };
  198. function handleClickBlank(e) {
  199. console.log('handleClickBlank');
  200. e.preventDefault();
  201. e.stopPropagation();
  202. visibleEmoji.value = false;
  203. commentActive.value = true;
  204. }
  205. function handleShowEmoji(e) {
  206. console.log('handleShowEmoji');
  207. e.preventDefault();
  208. e.stopPropagation();
  209. visibleEmoji.value = !visibleEmoji.value;
  210. }
  211. const emojiIndex = inject('$globalEmojiIndex')
  212. const { getHtml } = useEmojiHtml(emojiIndex);
  213. const commentHtml = computed(() => {
  214. let temp = myComment.value;
  215. if (!temp) {
  216. return '请输入你的评论,可以@成员';
  217. }
  218. return getHtml(temp);
  219. });
  220. const showHtml = ref(false);
  221. function handleClickHtmlShower(e) {
  222. e.preventDefault();
  223. e.stopPropagation();
  224. showHtml.value = false;
  225. commentRef.value.focus();
  226. console.log(234);
  227. commentActive.value = true;
  228. }
  229. function handleBlur() {
  230. showHtml.value = true;
  231. }
  232. const commentActive = ref(false);
  233. const noConent = computed(()=>{
  234. if(myComment.value.length>0){
  235. return false;
  236. }
  237. return true;
  238. });
  239. function changeActive(){
  240. if(myComment.value.length==0){
  241. commentActive.value = false
  242. uploadVisible.value = false;
  243. }
  244. }
  245. function selectFirstFile(fileName){
  246. if(myComment.value.length==0){
  247. myComment.value = fileName;
  248. }
  249. }
  250. return {
  251. myComment,
  252. sendComment,
  253. noComment,
  254. disabledButton,
  255. buttonLoading,
  256. commentRef,
  257. registerModal,
  258. openSelectUser,
  259. setValue,
  260. handleCommentChange,
  261. uploadRef,
  262. uploadVisible,
  263. onSelectEmoji,
  264. optionsName,
  265. emojiButton,
  266. emojiIndex,
  267. showEmoji,
  268. pickerStyles,
  269. visibleEmoji,
  270. handleClickBlank,
  271. handleShowEmoji,
  272. commentHtml,
  273. showHtml,
  274. handleClickHtmlShower,
  275. handleBlur,
  276. commentActive,
  277. noConent,
  278. changeActive,
  279. selectFirstFile
  280. };
  281. },
  282. };
  283. </script>
  284. <style lang="less">
  285. .comment-content {
  286. box-sizing: border-box;
  287. margin: 0;
  288. padding: 0;
  289. font-variant: tabular-nums;
  290. list-style: none;
  291. font-feature-settings: tnum;
  292. position: relative;
  293. display: inline-block;
  294. width: 100%;
  295. padding: 4px 11px;
  296. color: rgba(0, 0, 0, 0.85);
  297. font-size: 15px;
  298. line-height: 1.5715;
  299. background-color: #fff;
  300. background-image: none;
  301. border: 1px solid #d9d9d9;
  302. border-radius: 2px;
  303. transition: all 0.3s;
  304. width: 100%;
  305. border: solid 0px;
  306. outline: none;
  307. .emoji-item {
  308. display: inline-block !important;
  309. width: 0 !important;
  310. }
  311. }
  312. .comment-buttons {
  313. padding: 10px;
  314. display: flex;
  315. justify-content: space-between;
  316. border-top: 1px solid #d9d9d9;
  317. .anticon {
  318. margin: 5px;
  319. }
  320. }
  321. .comment-html-shower {
  322. position: absolute;
  323. top: 0;
  324. left: 0;
  325. height: 70px;
  326. &.bottom-div {
  327. z-index: -99;
  328. }
  329. &.top-div {
  330. z-index: 9;
  331. }
  332. }
  333. .emoji-modal {
  334. > .ant-modal{
  335. right: 25% !important;
  336. margin-right: 16px !important;
  337. }
  338. .ant-modal-header{
  339. padding: 0 !important;
  340. }
  341. .emoji-mart-bar{
  342. display: none;
  343. }
  344. h3.emoji-mart-category-label{
  345. /* display: none;*/
  346. border-bottom: 1px solid #eee;
  347. }
  348. }
  349. .comment-active{
  350. border-color: #1e88e5 !important;
  351. box-shadow: 0 1px 1px 0 #90caf9, 0 1px 6px 0 #90caf9;
  352. }
  353. .no-content{
  354. color: #a1a1a1
  355. }
  356. /**聊天表情本地化*/
  357. .emoji-type-image.emoji-set-apple {
  358. background-image: url("./image/emoji.png");
  359. }
  360. </style>