index.vue 15 KB

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