index.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <!-- -->
  2. <template>
  3. <div class="warpper">
  4. <div class="write-box" id="container">
  5. <!-- <div @click="onConfirm">确定</div> -->
  6. <!-- -->
  7. <div class="content" id="content" :style="`height:${inputHeight}px;`">
  8. <!-- <div contenteditable v-html="text" :style="`height:${lineCount * 40}px;`" @keydown="hanlderWrite($event)" id="write-info"></div> -->
  9. <textarea maxLength="1000" :style="`height:${lineCount * 40}px;`" @paste="onPaste" @keydown="hanlderWrite($event)" id="write-info" v-model="inputText"> </textarea>
  10. <div id="msg" class="msg-box">{{ inputText }}</div>
  11. <div class="item" :style="`top:${index * 40}px;`" v-for="(i, index) in lineCount"></div>
  12. </div>
  13. </div>
  14. </div>
  15. </template>
  16. <script setup>
  17. import { reactive, ref, toRefs, onBeforeMount, computed, onMounted, nextTick, defineEmits, defineProps } from "vue";
  18. const props = defineProps({
  19. text: {
  20. type: String,
  21. default: "",
  22. },
  23. textIndex: {
  24. type: Number,
  25. default: 0,
  26. },
  27. });
  28. const emits = defineEmits(["onTextConfirm", "onTextChange"]);
  29. const inputText = ref("");
  30. const lineCount = ref(1);
  31. const inputHeight = ref(0);
  32. const getLineCount = () => {
  33. let containerH = document.getElementById("container").clientHeight;
  34. let count = Math.floor(containerH / 40);
  35. lineCount.value = count;
  36. inputHeight.value = count * 40;
  37. // textAreaHeight.value = lineCount.value * 40;
  38. };
  39. const onConfirm = () => {
  40. let page = 1;
  41. if (msgHeight.value < 400) {
  42. page = 1;
  43. } else {
  44. page = page + Math.ceil((msgHeight.value - 400) / 1080);
  45. }
  46. emits("onTextConfirm", { text: inputText.value, msgHeight: msgHeight.value, page });
  47. };
  48. const msgHeight = ref(40);
  49. defineExpose({ onConfirm });
  50. const hanlderWrite = (e) => {
  51. let msgH = document.getElementById("msg").clientHeight;
  52. msgHeight.value = msgH;
  53. let msgCount = Math.floor(msgH / 40);
  54. let containerH = document.getElementById("container").clientHeight;
  55. let containerCount = Math.floor(containerH / 40);
  56. // text.value = e.target.innerHTML;
  57. if (msgCount > containerCount) {
  58. if (e && e.keyCode == 13) {
  59. msgCount++;
  60. }
  61. lineCount.value = msgCount;
  62. } else {
  63. lineCount.value = containerCount;
  64. }
  65. emits("onTextChange", { text: inputText.value });
  66. // textAreaHeight.value = msgH;
  67. };
  68. const onPaste = (e) => {
  69. setTimeout(() => {
  70. // let msgH = document.getElementById('msg').clientHeight;
  71. hanlderWrite(e);
  72. let content = document.getElementById("content");
  73. content.scrollTo(0, 9999999);
  74. }, 100);
  75. };
  76. const setFocusAt = (focusIndex) => {
  77. let txtarea = document.getElementById("write-info");
  78. setCaretPosition(txtarea, focusIndex);
  79. };
  80. //设置光标位置
  81. const setCaretPosition = (ctrl, pos) => {
  82. if (ctrl.setSelectionRange) {
  83. ctrl.focus();
  84. ctrl.setSelectionRange(pos, pos);
  85. } else if (ctrl.createTextRange) {
  86. var range = ctrl.createTextRange();
  87. range.collapse(true);
  88. range.moveEnd("character", pos);
  89. range.moveStart("character", pos);
  90. range.select();
  91. }
  92. nextTick(() => {
  93. getSelectionDistance();
  94. });
  95. };
  96. // 获取 textarea 元素
  97. const getSelectionDistance = () => {
  98. var textarea = document.getElementById("write-info");
  99. // // 获取 textarea 的样式属性
  100. var textareaStyle = getComputedStyle(textarea);
  101. // // 计算光标位置距离顶部的距离
  102. var lineHeight = parseInt(textareaStyle.lineHeight, 10); // 获取行高,如果没有设置,会返回默认值
  103. // 获取光标的起始位置
  104. var cursorPosition = textarea.selectionStart;
  105. // 获取 textarea 内容
  106. var textareaValue = textarea.value;
  107. // 将内容分割成行
  108. var lines = textareaValue.split("\n");
  109. // 计算光标所在的行数1
  110. var cursorLine = 1; // 初始化行数为1
  111. for (var i = 0; i < lines.length; i++) {
  112. if (cursorPosition <= lines[i].length) {
  113. break; // 找到光标所在的行后退出循环
  114. }
  115. cursorPosition -= lines[i].length + 1; // 加1是为了包括换行符
  116. cursorLine++;
  117. }
  118. let scrollBox = document.getElementById("content");
  119. let scrollHeight = cursorLine * lineHeight;
  120. let scrollBoxHeight = scrollBox.offsetHeight;
  121. if (scrollHeight > scrollBoxHeight) {
  122. console.log("光标所在的行数:", cursorLine);
  123. scrollBox.scrollTo(0, scrollHeight - scrollBoxHeight);
  124. }
  125. };
  126. onMounted(async () => {
  127. await nextTick();
  128. getLineCount();
  129. inputText.value = props.text;
  130. setTimeout(() => {
  131. hanlderWrite();
  132. if (props.text && props.textIndex) {
  133. // console.error(props.textIndex);
  134. setFocusAt(props.textIndex);
  135. }
  136. }, 0);
  137. nextTick(() => {
  138. let scrollBox = document.getElementById("content");
  139. scrollBox.addEventListener("scroll", (data) => {
  140. // console.error(data);
  141. });
  142. });
  143. });
  144. </script>
  145. <style lang="scss" scoped>
  146. .warpper {
  147. padding: 70px 50px 30px;
  148. }
  149. .write-box {
  150. width: 100%;
  151. height: calc(100vh - 100px);
  152. font-size: 24px;
  153. color: #000;
  154. overflow: hidden;
  155. font-family: SimSun-Regular, SimSun;
  156. .content {
  157. overflow-y: auto;
  158. overflow-x: hidden;
  159. padding-bottom: 5px;
  160. position: relative;
  161. .item {
  162. width: 100%;
  163. height: 40px;
  164. border-bottom: 1px solid #000;
  165. box-sizing: border-box;
  166. position: absolute;
  167. left: 0;
  168. }
  169. #write-info {
  170. position: absolute;
  171. top: 0;
  172. left: 0;
  173. width: 100%;
  174. height: 100%;
  175. line-height: 40px;
  176. outline: none;
  177. resize: none;
  178. z-index: 2;
  179. overflow: hidden;
  180. font-size: 24px;
  181. font-family: SimSun-Regular, SimSun;
  182. }
  183. .msg-box {
  184. min-height: 40px;
  185. white-space: pre-wrap;
  186. opacity: 0;
  187. position: absolute;
  188. width: 100%;
  189. line-height: 40px;
  190. z-index: 1;
  191. }
  192. }
  193. }
  194. </style>