index.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. <template>
  2. <div v-if="option.opreaState" class="oprea">
  3. <div class="header">
  4. <div style="display: flex">
  5. <span v-if="option.out" class="btn" @click="downloadExcel">导出</span>
  6. <!-- <el-button
  7. type="primary"
  8. class="btn"
  9. v-if="option.out"
  10. @click="print"
  11. v-print="'#print_html'"
  12. >打印</el-button
  13. > -->
  14. <el-upload
  15. v-if="option.in"
  16. ref="upload"
  17. :auto-upload="false"
  18. :on-change="leadingExcel"
  19. :show-file-list="false"
  20. accept=".xlsx"
  21. class="in"
  22. >
  23. <!-- <template #trigger>
  24. <span class="btn">导入</span>
  25. </template>-->
  26. </el-upload>
  27. <span class="btn" v-if="option.set" @click="toHardware">开始测试</span>
  28. </div>
  29. <div class="info">当前操作表格:{{ inName == "" ? "-" : inName }}</div>
  30. </div>
  31. </div>
  32. <div
  33. id="luckysheet"
  34. :style="{
  35. top:
  36. option.opreaState == false ||
  37. option.edit == false ||
  38. (option.in == false && option.out == false && option.confirm == false)
  39. ? 0
  40. : 60,
  41. }"
  42. ></div>
  43. <div v-show="isMaskShow" id="tip">Downloading</div>
  44. <div id="print_html" style="text-align: center"></div>
  45. </template>
  46. <script setup>
  47. import { onMounted, ref, watch } from "vue";
  48. import { exportExcel } from "./export";
  49. import { getExcelData } from "@/api/excel";
  50. import LuckyExcel from "luckyexcel";
  51. import resData from "./resetData";
  52. const props = defineProps({
  53. //双向绑定的data
  54. data: {
  55. type: [Object, null],
  56. default: null,
  57. },
  58. //配置项
  59. option: {
  60. type: Object,
  61. default: () => ({
  62. //控制上部分展示
  63. opreaState: true,
  64. set: false,
  65. in: true,
  66. out: true,
  67. print: true,
  68. edit: true,
  69. inName: "",
  70. }),
  71. },
  72. //校验区域
  73. verifications: {
  74. type: Array,
  75. default: () => [],
  76. },
  77. //校验状态
  78. checkStatus: {
  79. type: Boolean,
  80. default: false,
  81. },
  82. });
  83. const toHardware = async () => {
  84. const { data, code } = await getExcelData();
  85. if (code == "200") {
  86. ElMessage.success("硬件调取成功!");
  87. }
  88. };
  89. //update:data : v-model表格data ,confirm : 获取此时表格data
  90. const emits = defineEmits(["update:data", "confirm"]);
  91. //新增默认表格data
  92. const resetdata = JSON.parse(resData);
  93. //展示名称
  94. const inName = ref("");
  95. //loading 变量
  96. const isMaskShow = ref(false);
  97. //表格初始化默认值
  98. const resetOb = ref({
  99. container: "luckysheet", // 设定DOM容器的id
  100. title: "Luckysheet Demo", // 设定表格名称
  101. lang: "zh", // 设定表格语言
  102. enableAddBackTop: true, //返回头部按钮
  103. userInfo: false, // 右上角的用户信息展示样式
  104. showinfobar: false, // 是否显示顶部信息栏
  105. showConfigWindowResize: true, // 自动缩进界面
  106. column: 30,
  107. row: 30,
  108. showsheetbar: false, // 是否显示底部sheet页按钮
  109. showsheetbarConfig: {
  110. add: false, //新增sheet
  111. menu: false, //sheet管理菜单
  112. sheet: false, //sheet页显示
  113. print: false,
  114. },
  115. cellRightClickConfig: {
  116. //右键单元格菜单设置
  117. copy: true, // 复制
  118. copyAs: true, // 复制为
  119. paste: true, // 粘贴
  120. insertRow: true, // 插入行
  121. insertColumn: true, // 插入列
  122. deleteRow: true, // 删除选中行
  123. deleteColumn: true, // 删除选中列
  124. deleteCell: true, // 删除单元格
  125. hideRow: true, // 隐藏选中行和显示选中行
  126. hideColumn: true, // 隐藏选中列和显示选中列
  127. rowHeight: true, // 行高
  128. columnWidth: true, // 列宽
  129. clear: true, // 清除内容
  130. matrix: true, // 矩阵操作选区
  131. sort: true, // 排序选区
  132. filter: true, // 筛选选区
  133. chart: true, // 图表生成
  134. image: true, // 插入图片
  135. link: true, // 插入链接
  136. data: true, // 数据验证
  137. cellFormat: true, // 设置单元格格式
  138. },
  139. data: null,
  140. });
  141. //print
  142. const print = () => {
  143. console.log(luckysheet);
  144. $("#luckysheet-left-top").click();
  145. // 2. 生成选区的截图
  146. let src = luckysheet.getScreenshot();
  147. let $img = `<img src=${src} style="max-width: 90%;" />`;
  148. nextTick(() => {
  149. document.querySelector("#print_html").innerHTML = $img;
  150. });
  151. };
  152. //设置第一个sheet
  153. const getActiveSheet = (sheets = []) => {
  154. let data = [];
  155. // 取激活项
  156. sheets.some((item) => {
  157. if (item.status === "1") {
  158. data.push(item);
  159. return true;
  160. }
  161. });
  162. // 没有激活项,取第一项
  163. if (data.length === 0) {
  164. data.push(sheets[0]);
  165. }
  166. return data;
  167. };
  168. //导入
  169. const leadingExcel = (item, fileList) => {
  170. const file = item.raw;
  171. if (file == null || file.name == "") {
  172. return;
  173. }
  174. let name = file.name;
  175. let suffixArr = name.split("."),
  176. suffix = suffixArr[suffixArr.length - 1];
  177. if (suffix != "xlsx") {
  178. ElMessage.error("请导入xlsx格式的文件");
  179. return;
  180. }
  181. LuckyExcel.transformExcelToLucky(file, function (exportJson, luckysheetfile) {
  182. if (exportJson.sheets == null || exportJson.sheets.length == 0) {
  183. ElMessage.error("请导入xlsx格式的文件");
  184. return;
  185. }
  186. window.luckysheet.destroy();
  187. const data = getActiveSheet(exportJson.sheets);
  188. resetOb.value.data = data;
  189. emits("update:data", data);
  190. inName.value = file.name;
  191. window.luckysheet.create(resetOb.value);
  192. });
  193. };
  194. //导出
  195. const downloadExcel = () => {
  196. exportExcel(luckysheet.getAllSheets(), "导出表格");
  197. };
  198. //获取此时表格数据
  199. const confirm = () => emits("confirm", luckysheet.getAllSheets());
  200. const getData = () => luckysheet.getAllSheets();
  201. //销毁
  202. const reset = () => {
  203. luckysheet.destroy();
  204. };
  205. //保存当次cell数据
  206. const saveCellData = () => {
  207. const enter = () => {
  208. let event = new KeyboardEvent("keydown", {
  209. key: "Enter",
  210. code: "Enter",
  211. keyCode: 13,
  212. which: 13,
  213. shiftKey: false,
  214. ctrlKey: false,
  215. altKey: false,
  216. metaKey: false,
  217. bubbles: true,
  218. cancelable: true,
  219. });
  220. // 分发事件到document
  221. document.dispatchEvent(event);
  222. };
  223. enter();
  224. };
  225. //配置单元格校验
  226. const setVerification = () => {
  227. for (let i = 0; i < props.verifications.length; i++) {
  228. if (
  229. !props.verifications[i].checkStr ||
  230. props.verifications[i].checkStr === ""
  231. ) {
  232. } else {
  233. let option = {
  234. type: "number",
  235. type2: "bw",
  236. value1: JSON.parse(props.verifications[i].checkStr).down,
  237. value2: JSON.parse(props.verifications[i].checkStr).up,
  238. hintShow: true,
  239. hintText: `请输入${JSON.parse(props.verifications[i].checkStr).down}-${JSON.parse(props.verifications[i].checkStr).up}的数字`,
  240. };
  241. luckysheet.setDataVerification(
  242. { ...option },
  243. { range: props.verifications[i].position }
  244. );
  245. }
  246. }
  247. };
  248. const setVal = (obj) => {
  249. Object.keys(obj).forEach((key) => {
  250. let val = obj[key];
  251. const { row, col } = getRowCol(key);
  252. luckysheet.setCellValue(row, col, val);
  253. });
  254. };
  255. const getRowCol = (str) => {
  256. const [letterPart, numberPart] = str.match(/([A-Z]+)(\d+)/).slice(1);
  257. let col = 0;
  258. for (let i = 0; i < letterPart.length; i++) {
  259. col = col * 26 + (letterPart.charCodeAt(i) - "A".charCodeAt(0));
  260. }
  261. const row = parseInt(numberPart, 10) - 1;
  262. console.log(row, col);
  263. return { row, col };
  264. };
  265. defineExpose({ confirm, reset, saveCellData, getData });
  266. onMounted(() => {
  267. if (props.data == null) {
  268. inName.value = "表格模版";
  269. resetOb.value.data = resetdata;
  270. } else {
  271. inName.value = props.option.inName;
  272. resetOb.value.data = props.data;
  273. }
  274. if (props.option.edit == false) {
  275. resetOb.value.allowEdit = false;
  276. }
  277. luckysheet.create(resetOb.value);
  278. });
  279. watch(
  280. () => props.verifications,
  281. () => {
  282. nextTick(() => {
  283. if (props.checkStatus == true && props.verifications.length > 0) {
  284. setVerification();
  285. }
  286. window.luckysheet.setVal = setVal;
  287. });
  288. },
  289. { immediate: true }
  290. );
  291. </script>
  292. <style lang="scss" scoped>
  293. .oprea {
  294. padding: 0 20px;
  295. width: 100%;
  296. height: 60px;
  297. .header {
  298. display: flex;
  299. align-items: center;
  300. justify-content: space-between;
  301. .in {
  302. display: inline-block;
  303. margin-top: 1px;
  304. }
  305. .btn {
  306. border-radius: 16px;
  307. height: 40px;
  308. font-size: 20px;
  309. margin: 0 10px;
  310. border: 2px dashed #000;
  311. font-weight: 500;
  312. @include flex;
  313. }
  314. .info {
  315. width: 260px;
  316. }
  317. }
  318. }
  319. #luckysheet {
  320. margin: 0px;
  321. padding: 0px;
  322. position: absolute;
  323. width: 100%;
  324. left: 0px;
  325. top: 50px;
  326. bottom: 0px;
  327. }
  328. #uploadBtn {
  329. font-size: 16px;
  330. }
  331. #tip {
  332. position: absolute;
  333. z-index: 1000000;
  334. left: 0px;
  335. top: 0px;
  336. bottom: 0px;
  337. right: 0px;
  338. background: rgba(255, 255, 255, 0.8);
  339. text-align: center;
  340. font-size: 40px;
  341. align-items: center;
  342. justify-content: center;
  343. display: flex;
  344. }
  345. </style>