index.vue 9.0 KB

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