MyComment.vue 10 KB

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