index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. <template>
  2. <div :id="`table-form-item-${keyId}`" v-loading="isLoading" :style="tableFormStyle"
  3. class="hc-table-form-data-item">
  4. <el-scrollbar v-if="isScroll" class="table-form-item-scrollbar">
  5. <div :id="`table-form-${keyId}`" class="hc-excel-table-form"/>
  6. </el-scrollbar>
  7. <div v-else :id="`table-form-${keyId}`" class="hc-excel-table-form"/>
  8. <div v-if="isTableForm === false" class="hc-no-table-form">
  9. <div class="table-form-no">
  10. <img :src="notableform" alt=""/>
  11. <div class="desc">{{ noTips }}</div>
  12. </div>
  13. </div>
  14. </div>
  15. </template>
  16. <script setup>
  17. import {ref, watch, onMounted, nextTick, onUnmounted} from "vue"
  18. import HTableForm from "~src/plugins/HTableForm"
  19. import notableform from '~src/assets/view/notableform.svg'
  20. import {getStoreData, setStoreData, delStoreData} from "~uti/storage";
  21. import {getObjNullValue, isString, deepClone, getIndex} from "vue-utils-plus"
  22. //初始
  23. const props = defineProps({
  24. pkey: [String, Number],
  25. noTip: {
  26. type: [String, Number],
  27. default: '暂无表单数据'
  28. },
  29. html: String,
  30. form: {
  31. type: Object,
  32. default: () => ({})
  33. },
  34. cols: {
  35. type: Array,
  36. default: () => ([])
  37. },
  38. loading: Boolean,
  39. scroll: {
  40. type: Boolean,
  41. default: true
  42. },
  43. height: {
  44. type: String,
  45. default: '100%'
  46. },
  47. width: {
  48. type: String,
  49. default: 'auto'
  50. },
  51. isMessage: {
  52. type: Boolean,
  53. default: true
  54. },
  55. })
  56. //初始变量
  57. const keyId = ref(props.pkey)
  58. const noTips = ref(props.noTip)
  59. const isScroll = ref(props.scroll)
  60. const isLoading = ref(props.loading)
  61. //表单数据
  62. const excelHtml = ref(props.html)
  63. const excelForm = ref(props.form)
  64. const colsKeys = ref(props.cols)
  65. //样式
  66. const tableFormApp = ref(null)
  67. const tableFormStyle = ref('')
  68. const isTableForm = ref(false)
  69. //监听
  70. watch(() => [props.pkey, props.noTip, props.scroll, props.loading, props.width, props.height], ([pkey, tip, scroll, loading, width, height]) => {
  71. keyId.value = pkey
  72. noTips.value = tip
  73. isScroll.value = scroll
  74. isLoading.value = loading
  75. setItemStyle(width, height)
  76. })
  77. //html变动
  78. watch(() => [
  79. props.html
  80. ], ([html]) => {
  81. excelHtml.value = html
  82. setExcelHtml()
  83. })
  84. //深度监听变动的对象数据
  85. watch(() => [
  86. props.form
  87. ], ([val]) => {
  88. excelForm.value = val
  89. setPickerKey()
  90. setFormData(val)
  91. }, {
  92. deep: true
  93. })
  94. //深度监听按键切换的key列表
  95. watch(() => [
  96. props.cols
  97. ], ([cols]) => {
  98. colsKeys.value = cols
  99. }, {
  100. deep: true
  101. })
  102. //渲染完成
  103. onMounted(() => {
  104. setItemStyle(props.width, props.height)
  105. setPickerKey()
  106. getExcelHtml()
  107. })
  108. //设置样式
  109. const setItemStyle = (width, height) => {
  110. tableFormStyle.value = `width: ${width}; height: ${height};`
  111. }
  112. //事件
  113. const emit = defineEmits(['rightTap', 'render'])
  114. //获取已填写的数据
  115. const setPickerKey = () => {
  116. HTableForm.setPickerKey(excelForm.value)
  117. }
  118. const setExcelHtml = () => {
  119. //先卸载
  120. if (tableFormApp.value) {
  121. tableFormApp.value?.unmount()
  122. }
  123. setPickerKey()
  124. getExcelHtml()
  125. }
  126. //获取模板标签数据
  127. const getExcelHtml = () => {
  128. const pkeyId = keyId.value
  129. const temp = isString(excelHtml.value) ? excelHtml.value : ''
  130. if (temp && pkeyId) {
  131. isTableForm.value = true
  132. //渲染表单
  133. tableFormApp.value = HTableForm.createForm({
  134. template: temp,
  135. tableForm: excelForm.value,
  136. keys: colsKeys.value,
  137. appId: `#table-form-${pkeyId}`,
  138. onRight: (event, KeyName) => {
  139. onRightClick(pkeyId, event, KeyName)
  140. },
  141. //表单正则效验
  142. onBlur: (event, key, reg, val, msg) => {
  143. setTableFormBlurReg(pkeyId, event, key, reg, val, msg)
  144. },
  145. onLeftClick: (key) => {
  146. setShiftTableForm(key)
  147. },
  148. })
  149. excelForm.value.isRenderForm = true
  150. nextTick(() => {
  151. HTableForm.setByClassKeyup(colsKeys.value)
  152. })
  153. } else {
  154. isTableForm.value = false
  155. excelForm.value.isRenderForm = false
  156. }
  157. emit('render', excelForm.value)
  158. }
  159. //正则效验
  160. const formRegExpJson = ref({})
  161. const setTableFormBlurReg = (pkeyId, event, key, reg, val, msg) => {
  162. const dom = document.getElementById(key)?.parentElement ?? ''
  163. if (dom) {
  164. if (val && reg) {
  165. let regx = new RegExp(reg);
  166. let state = regx.test(val);
  167. if (state) {
  168. delete formRegExpJson.value[key]
  169. HTableForm.setFormStyle(key, 'hc-red-border')
  170. } else {
  171. formRegExpJson.value[key] = {key, reg, val, msg, state}
  172. HTableForm.setFormStyle(key, 'hc-red-border', true)
  173. window?.$message?.warning(msg)
  174. }
  175. } else {
  176. delete formRegExpJson.value[key]
  177. HTableForm.setFormStyle(key, 'hc-red-border')
  178. }
  179. } else {
  180. delete formRegExpJson.value[key]
  181. }
  182. }
  183. //鼠标右键事件
  184. const onRightClick = (pkeyId, event, KeyName) => {
  185. //取光标位置
  186. const specialDom = document.getElementById(KeyName + "")
  187. const startPos = specialDom?.selectionStart || 0
  188. const endPos = specialDom?.selectionEnd || 0
  189. emit('rightTap', {event, KeyName, startPos, endPos, pkeyId})
  190. }
  191. const isCtrlKey = ref(false)
  192. const checkKeyList = ref([])
  193. const copyKeyList = ref(getStoreData('TableFormCopyKeyList') || [])
  194. //设置选择数据
  195. const setShiftTableForm = (key) => {
  196. if (isCtrlKey.value) {
  197. const form = excelForm.value
  198. const keys = checkKeyList.value
  199. const index = getIndex(keys, 'key', key)
  200. if (index === -1) {
  201. keys.push({key: key, val: form[key]})
  202. } else {
  203. keys.splice(index, 1)
  204. }
  205. checkKeyList.value = keys
  206. HTableForm.setCheckKeyStyle(key, index !== -1)
  207. }
  208. }
  209. //全局按键按下监听
  210. document.onkeydown = async (event) => {
  211. const {key, ctrlKey} = event
  212. //按下ctrl键
  213. if (ctrlKey && key === 'Control') {
  214. isCtrlKey.value = true
  215. }
  216. //按下复制快捷键
  217. if (ctrlKey && key === 'c') {
  218. const keysList = deepClone(checkKeyList.value)
  219. if (keysList.length > 0) {
  220. event.preventDefault()
  221. setStoreData('TableFormCopyKeyList', keysList)
  222. copyKeyList.value = keysList
  223. keysList.forEach(item => {
  224. HTableForm.setCheckKeyStyle(item['key'], true)
  225. })
  226. checkKeyList.value = []
  227. }
  228. }
  229. //按下粘贴快捷键
  230. if (ctrlKey && key === 'v') {
  231. const keysList = deepClone(copyKeyList.value)
  232. const checkList = checkKeyList.value
  233. if (checkList.length > 0) {
  234. event.preventDefault()
  235. //粘贴多个
  236. if (checkList.length > 1 && keysList.length > 1) {
  237. await setMultipleCheckValue(checkList, keysList)
  238. }
  239. //粘贴单个复制
  240. if (checkList.length > 1 && keysList.length === 1) {
  241. await setSingleCopyValue(checkList, keysList)
  242. }
  243. //复制单个粘贴
  244. if (checkList.length === 1 && keysList.length > 1) {
  245. await setCopySingleValue(checkList, keysList)
  246. }
  247. //清除缓存
  248. checkKeyList.value = []
  249. copyKeyList.value = []
  250. delStoreData('TableFormCopyKeyList')
  251. }
  252. }
  253. }
  254. //复制单个粘贴
  255. const setCopySingleValue = async (checkList, keysList) => {
  256. let form_val = '', key = checkList[0]['key']
  257. const form = excelForm.value
  258. for (let i = 0; i < keysList.length; i++) {
  259. const val = form[keysList[i]['key']]
  260. form_val = form_val ? form_val + '、' + val : val
  261. keysList.splice(0, 1) //删除第一个元素
  262. }
  263. await setTableFormInfoValue(key, form_val)
  264. HTableForm.setCheckKeyStyle(key, true)
  265. }
  266. //粘贴单个复制
  267. const setSingleCopyValue = async (checkList, keysList) => {
  268. const form = excelForm.value
  269. const form_val = form[keysList[0]['key']]
  270. for (let i = 0; i < checkList.length; i++) {
  271. const {key} = checkList[i]
  272. await setTableFormInfoValue(key, form_val)
  273. HTableForm.setCheckKeyStyle(key, true)
  274. }
  275. keysList.splice(0, 1) //删除第一个元素
  276. }
  277. //粘贴多个
  278. const setMultipleCheckValue = async (checkList, keysList) => {
  279. const form = excelForm.value
  280. for (let i = 0; i < checkList.length; i++) {
  281. const {key, val} = checkList[i]
  282. if (keysList.length > 0) {
  283. const form_val = form[keysList[0]['key']]
  284. await setTableFormInfoValue(key, form_val ? form_val : val)
  285. keysList.splice(0, 1) //删除第一个元素
  286. } else {
  287. await setTableFormInfoValue(key, val)
  288. }
  289. HTableForm.setCheckKeyStyle(key, true)
  290. }
  291. }
  292. //设置表单数据
  293. const setTableFormInfoValue = async (key, value) => {
  294. return new Promise((resolve) => {
  295. setTimeout(() => {
  296. excelForm.value[key] = value
  297. resolve(true)
  298. }, 100)
  299. })
  300. }
  301. //全局键盘放开监听
  302. document.onkeyup = (event) => {
  303. const {key, ctrlKey} = event
  304. if (!ctrlKey && key === 'Control') {
  305. isCtrlKey.value = false
  306. }
  307. }
  308. //卸载页面
  309. onUnmounted(() => {
  310. document.onkeydown = null
  311. document.onkeyup = null
  312. })
  313. //获取表单数据
  314. const getFormData = () => {
  315. return excelForm.value
  316. }
  317. //设置表单数据
  318. const setFormData = (data) => {
  319. nextTick(() => {
  320. excelForm.value = data
  321. })
  322. }
  323. //获取表单效验数据
  324. const getRegExpJson = () => {
  325. return deepClone(formRegExpJson.value);
  326. }
  327. //正则效验
  328. const isFormRegExp = async () => {
  329. const isRegExp = !!getObjNullValue(formRegExpJson.value)
  330. if (!isRegExp) {
  331. return true
  332. } else {
  333. window?.$message?.warning('请先修改完红色输入框的数据')
  334. return false
  335. }
  336. }
  337. //卸载渲染
  338. const unmountHtml = () => {
  339. if (tableFormApp.value) {
  340. tableFormApp.value?.unmount()
  341. }
  342. }
  343. // 暴露出去
  344. defineExpose({
  345. getFormData,
  346. setFormData,
  347. getRegExpJson,
  348. isFormRegExp,
  349. unmountHtml
  350. })
  351. </script>
  352. <style lang="scss">
  353. //插入特殊字符弹窗的输入框
  354. .hc-table-form-data-item .hc-excel-table-form td,
  355. .hc-table-form-data-item .hc-excel-table-form td .el-input .el-input__wrapper .el-input__inner,
  356. .el-form-item.special-form-item .el-form-item__content .el-input .el-input__wrapper .el-input__inner {
  357. font-family: "EUDC", 宋体, v-sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
  358. }
  359. .hc-table-form-data-item {
  360. position: relative;
  361. padding: 12px;
  362. height: 100%;
  363. overflow: hidden;
  364. background-color: white;
  365. .hc-excel-table-form {
  366. position: relative;
  367. display: flex;
  368. padding: 10px;
  369. justify-content: center;
  370. td {
  371. padding: 6px;
  372. .el-input {
  373. background-color: #ffffff !important;
  374. border-radius: 3px;
  375. color: #606266;
  376. .el-input__wrapper {
  377. background-color: inherit;
  378. caret-color: var(--el-color-primary);
  379. }
  380. .el-input__suffix-inner {
  381. width: 18px;
  382. }
  383. }
  384. .el-textarea {
  385. width: 100%;
  386. height: 100%;
  387. .el-textarea__inner {
  388. min-height: initial !important;
  389. background-color: #ffffff;
  390. border-radius: 3px;
  391. color: #606266;
  392. height: 100%;
  393. caret-color: var(--el-color-primary);
  394. }
  395. }
  396. //日期选择框
  397. .el-date-editor.el-input .el-input__wrapper,
  398. .el-date-editor.el-date-editor--datetimerange.el-input__wrapper {
  399. height: 100%;
  400. width: 100%;
  401. }
  402. //焦点
  403. .el-input .el-input__wrapper.is-focus, .el-input .el-input__wrapper:hover,
  404. .el-textarea .el-textarea__inner:hover {
  405. box-shadow: 0 0 0 1.5px var(--el-input-focus-border-color) inset;
  406. background-color: #eddac4;
  407. }
  408. //公式
  409. &[gscolor] {
  410. .el-input, .el-textarea .el-textarea__inner {
  411. background-color: #dcdcdc !important;
  412. }
  413. }
  414. //文本选中颜色
  415. .el-input .el-input__wrapper input,
  416. .el-textarea textarea {
  417. &::selection {
  418. background: var(--el-color-primary-light-9);
  419. color: var(--el-color-primary);
  420. }
  421. &::-moz-selection {
  422. background: var(--el-color-primary-light-9);
  423. color: var(--el-color-primary);
  424. }
  425. }
  426. //下拉框
  427. .el-select {
  428. width: 100%;
  429. height: 100%;
  430. }
  431. }
  432. //非输入框颜色
  433. td:not([titlexx]), td[titlexx*=''],
  434. td:not([title]), td[title*=''] {
  435. background-color: white !important;
  436. user-select: none;
  437. }
  438. }
  439. .hc-no-table-form {
  440. position: relative;
  441. height: 100%;
  442. display: flex;
  443. justify-content: center;
  444. align-items: center;
  445. .table-form-no {
  446. position: relative;
  447. img {
  448. width: 350px;
  449. }
  450. .desc {
  451. text-align: center;
  452. font-size: 20px;
  453. color: #aaa;
  454. }
  455. }
  456. }
  457. }
  458. .hc-red-border {
  459. &.el-textarea__inner, &.el-input .el-input__wrapper {
  460. --el-input-border-color: #fe0000 !important;
  461. box-shadow: 0 0 0 2px #fe0000 inset !important;
  462. }
  463. }
  464. .hc-green-border {
  465. &.el-textarea__inner, &.el-input .el-input__wrapper {
  466. --el-input-border-color: #1ECC95 !important;
  467. box-shadow: 0 0 0 2px #1ECC95 inset !important;
  468. }
  469. }
  470. </style>