onlineExcelCopy.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. <template>
  2. <div class="excel-data-container" v-loading="saveLoading" element-loading-text="保存中,请稍后······" element-loading-spinner="el-icon-loading"
  3. element-loading-background="rgba(0, 0, 0, 0.8)">
  4. <div class="online-excel">
  5. <div id="luckysheet"></div>
  6. </div>
  7. <div class="data-options-box">
  8. <div class="data-options-buttons">
  9. <el-button type="primary" style="width: 80px;font-size: 16px;" @click="saveExcel">保存</el-button>
  10. <el-button plain style="width: 80px;font-size: 16px;" @click="cancel">取消</el-button>
  11. </div>
  12. <div class="data-options" >
  13. <el-form :model="dataForm" label-position="top" ref="dataOptions">
  14. <el-form-item label="一级分类" prop="firstClassify" :rules="[{required:true,message:'一级分类不能为空',trigger:'change'}]">
  15. <el-select v-model="dataForm.firstClassify" placeholder="请选择一级分类" style="width: 100%;" @change="changeFirstClassify">
  16. <el-option :label="item.ClassifyName" :value="item.ClassifyId" v-for="item in classifyArray" :key="item.ClassifyId"></el-option>
  17. </el-select>
  18. </el-form-item>
  19. <el-form-item label="指标分类" prop="secondClassify" :rules="[{required:true,message:'指标分类不能为空',trigger:'change'}]">
  20. <el-select v-model="dataForm.secondClassify" placeholder="请选择指标分类" style="width: 100%;" @change="changeSecondClassify">
  21. <el-option :label="item.ClassifyName" :value="item.ClassifyId" v-for="item in secondClassifyArray" :key="item.ClassifyId"></el-option>
  22. </el-select>
  23. </el-form-item>
  24. <el-form-item label="频度" prop="frequency" :rules="[{required:true,message:'频度不能为空',trigger:'change'}]">
  25. <el-select v-model="dataForm.frequency" placeholder="请选择频度" style="width: 100%;" @change="changeFrequency">
  26. <el-option :label="item" :value="item" v-for="item in frequencyList" :key="item"></el-option>
  27. </el-select>
  28. </el-form-item>
  29. </el-form>
  30. <el-tooltip :open-delay="400">
  31. <div slot="content">
  32. <p class="hint-message">1、新增指标:在预设好的指标列下输入指标名称、单位、数值并保存;</p>
  33. <p class="hint-message">2、新增日期:在第一列选择日期或插入日期行(需复制已有日期格式)输入数值并保存;</p>
  34. <p class="hint-message">3、删除指标:清空该指标下所有数据并保存,请勿直接删除指标列;</p>
  35. <p class="hint-message">4、删除日期:清空该日期对应所有数据并保存,请勿直接删除日期行;</p>
  36. </div>
  37. <div class="instruction-hint" >
  38. <span>使用说明</span>
  39. <i class="el-icon-question" style="color: #999;"></i>
  40. </div>
  41. </el-tooltip>
  42. </div>
  43. </div>
  44. </div>
  45. </template>
  46. <script>
  47. import { dataInterence } from "@/api/api.js";
  48. import EventBus from '@/api/bus.js'
  49. export default {
  50. name:'onlineExcelCopy',
  51. data() {
  52. return {
  53. // 初始化数据
  54. options:{
  55. container:'luckysheet',
  56. lang: 'zh',
  57. gridKey:'handmade-excel',
  58. showinfobar:false,
  59. row:300,
  60. column:60,
  61. data:[
  62. {
  63. "name":"test",
  64. "celldata":[
  65. {
  66. "r":0,
  67. "c":0,
  68. "v":{
  69. ct: {fa: "General", t: "g"},
  70. m:"一级分类",
  71. v:"一级分类",
  72. bg:'#b4a7d6'
  73. },
  74. },
  75. {
  76. "r":1,
  77. "c":0,
  78. "v":{
  79. ct: {fa: "General", t: "g"},
  80. m:"指标分类",
  81. v:"指标分类",
  82. bg:'#b4a7d6'
  83. },
  84. },
  85. {
  86. "r":2,
  87. "c":0,
  88. "v":{
  89. ct: {fa: "General", t: "g"},
  90. m:"频度",
  91. v:"频度",
  92. bg:'#b4a7d6'
  93. },
  94. },
  95. {
  96. "r":3,
  97. "c":0,
  98. "v":{
  99. ct: {fa: "General", t: "g"},
  100. m:"指标名称",
  101. v:"指标名称",
  102. bg:'#b4a7d6'
  103. },
  104. },
  105. {
  106. "r":4,
  107. "c":0,
  108. "v":{
  109. ct: {fa: "General", t: "g"},
  110. m:"单位",
  111. v:"单位",
  112. bg:'#b4a7d6'
  113. },
  114. },
  115. ],
  116. "config":{
  117. "borderInfo":[{
  118. "rangeType": "range",
  119. "borderType": "border-all",
  120. "style": "1",
  121. "color": "#fff",
  122. "range": [{
  123. "row": [0,4],
  124. "column": [0,0]
  125. }]
  126. }],
  127. },
  128. "frozen":{
  129. type: 'rangeBoth',
  130. range: {row_focus: 4, column_focus: 0}
  131. },
  132. "dataVerification":{}
  133. }
  134. ],
  135. hook:{
  136. cellUpdateBefore(r,c){
  137. // 禁止更新的单元格
  138. if([0,1,2,3,4].includes(r) && c==0){
  139. return false
  140. }
  141. },
  142. cellMousedownBefore(e,position){
  143. // 禁止鼠标选中的单元格
  144. if([0,1,2].includes(position.r) || ([3,4].includes(position.r) && position.c==0)){
  145. return false
  146. }
  147. }
  148. },
  149. showsheetbar:false
  150. },
  151. dataForm:{
  152. firstClassify:'',
  153. secondClassify:'',
  154. frequency:'',
  155. tradeCode:''
  156. },
  157. copyDataForm:{},
  158. firstClassName:'',
  159. secondClassName:'',
  160. frequencyList: ['日度','周度','旬度','月度','季度','半年度','年度'],
  161. classifyArray:[],
  162. unitList:[],
  163. secondClassifyArray:[],
  164. // 保存时候的加载框
  165. saveLoading:false,
  166. isEdit:"false"
  167. }
  168. },
  169. created() {
  170. this.dataForm.firstClassify = parseInt(this.$route.query.parentClassifyId) || ''
  171. this.dataForm.secondClassify = parseInt(this.$route.query.ClassifyId) || ''
  172. this.dataForm.frequency = this.$route.query.Frequency || ''
  173. this.dataForm.tradeCode = this.$route.query.TradeCode || ''
  174. this.dataForm.tradeName = this.$route.query.TradeName || ''
  175. this.copyDataForm = JSON.parse(JSON.stringify(this.dataForm))
  176. // 字符串
  177. this.isEdit = this.$route.query.isEdit.toString()
  178. this.initExcelOptions()
  179. // 监听折叠导航栏事件
  180. EventBus.$off('collapseHandle')
  181. EventBus.$on('collapseHandle',(params)=>{
  182. luckysheet.destroy()
  183. luckysheet.create(this.options)
  184. })
  185. },
  186. beforeRouteEnter(to, from, next){
  187. if(to.query.isEdit=='true'){
  188. to.matched[1].name='编辑excel数据'
  189. }else{
  190. to.matched[1].name='复制excel数据'
  191. }
  192. next()
  193. },
  194. beforeRouteLeave(to, from, next) {
  195. luckysheet.destroy()
  196. next();
  197. },
  198. // 监听浏览器返回事件
  199. mounted() {
  200. window.addEventListener('popstate',this.setCache)
  201. },
  202. unmounted() {
  203. window.removeEventListener('popstate',this.setCache)
  204. },
  205. methods: {
  206. async initExcelOptions(){
  207. // 获取单位接口
  208. await this.getTargetUnitList()
  209. // 获取分类
  210. await this.getClassify()
  211. let dataList = []
  212. // 编辑才请求数据
  213. if(this.dataForm.secondClassify && this.isEdit == "true"){
  214. let res = await dataInterence.getTargetList({
  215. Frequency: this.dataForm.frequency,
  216. ClassifyId: this.dataForm.secondClassify,
  217. TradeCode:this.dataForm.tradeCode,
  218. EdbShowType: this.dataForm.tradeCode?0:1,
  219. })
  220. if(res.Ret == 200){
  221. dataList = res.Data.List || []
  222. // if(res.Data.FrequencyList) this.dataForm.frequency = res.Data.FrequencyList[0]
  223. }
  224. }
  225. // 拿出所有数组
  226. let arr = dataList.map((item => item.DataList)).flat()
  227. // 拿出所有数组的日期并展平排序
  228. let dateArr = [...new Set(arr.map((item) => item.Dt))].sort().reverse()
  229. // 组合成需要的数据格式
  230. let excelData=dateArr.map(date =>{
  231. let objList = [date]
  232. dataList.forEach(element => {
  233. let obj = element.DataList.find((time) => time.Dt === date)
  234. objList.push(obj?obj.Close : '')
  235. });
  236. return objList
  237. })
  238. /**
  239. * 当数据的长度大于默认的84时,行数需要设置成数据的长度+15
  240. */
  241. this.options.row = dateArr.length>(this.options.row-15) ? (dateArr.length+15):this.options.row
  242. /**
  243. * 设置列数
  244. * 当数据的长度大于默认的60时,列数需要设置成数据的长度+10
  245. */
  246. this.options.column = dataList.length>(this.options.column-10) ? (dataList.length+10):this.options.column
  247. let row = this.options.row
  248. let col = this.options.column
  249. // 初始化数据
  250. dataList.map((item,index) =>{
  251. this.options.data[0].celldata.push(
  252. {
  253. "r":3,
  254. "c":1+index,
  255. "v":{
  256. ct: {fa: "General", t: "g"},
  257. m:item.SecName,
  258. v:item.SecName
  259. }
  260. },
  261. {
  262. "r":4,
  263. "c":1+index,
  264. "v":{
  265. ct: {fa: "General", t: "g"},
  266. m:item.Unit,
  267. v:item.Unit
  268. }
  269. })
  270. })
  271. for (let i = 0; i < col; i++) {
  272. // let item =dataList[0]
  273. this.options.data[0].celldata.push(
  274. {
  275. "r":0,
  276. "c":1+i,
  277. "v":{
  278. ct: {fa: "General", t: "g"},
  279. m:this.firstClassName || '',
  280. v:this.firstClassName || '',
  281. bg:'#cccccc'
  282. }
  283. },
  284. {
  285. "r":1,
  286. "c":1+i,
  287. "v":{
  288. ct: {fa: "General", t: "g"},
  289. m:this.secondClassName || '',
  290. v:this.secondClassName || '',
  291. bg:'#cccccc'
  292. }
  293. },
  294. {
  295. "r":2,
  296. "c":1+i,
  297. "v":{
  298. ct: {fa: "General", t: "g"},
  299. m:this.dataForm.frequency || '',
  300. v:this.dataForm.frequency || '',
  301. bg:'#cccccc'
  302. }
  303. })
  304. }
  305. for (let i = 0; i < row-5; i++) {
  306. for (let j = 0; j < (col+1); j++) {
  307. this.options.data[0].celldata.push(
  308. {
  309. "r":5+i,
  310. "c":0+j,
  311. "v":{
  312. ct: j==0?{fa: "yyyy-MM-dd", t: "d"}:{fa: "0.0000", t: "n"},
  313. m:excelData[i]?excelData[i][j]:'',
  314. v:excelData[i]?excelData[i][j]:'',
  315. }
  316. })
  317. }
  318. }
  319. // // 设置日期单元格颜色
  320. this.options.data[0].celldata.forEach(element => {
  321. if(element.c==0 && (![0,1,2,3,4].includes(element.r))){
  322. element.v.bg = '#ff9900'
  323. }
  324. });
  325. // 设置边框颜色
  326. this.options.data[0].config.borderInfo.push({
  327. "rangeType": "range",
  328. "borderType": "border-all",
  329. "style": "1",
  330. "color": "#fff",
  331. "range": [{
  332. "row": [0,2],
  333. "column": [1,col]
  334. }]
  335. },{
  336. "rangeType": "range",
  337. "borderType": "border-all",
  338. "style": "1",
  339. "color": "#fff",
  340. "range": [{
  341. "row": [5,row],
  342. "column": [0,0]
  343. }]
  344. })
  345. // 设置数据验证
  346. for (let i = 0; i < col; i++) {
  347. this.options.data[0].dataVerification[`4_${1+i}`]={
  348. "type": "dropdown",
  349. "type2": null,
  350. "value1": this.unitList.join(','),
  351. "value2": "",
  352. "prohibitInput": false,
  353. // "hintShow": false,
  354. // "hintText": ""
  355. }
  356. }
  357. this.$nextTick(()=>{
  358. luckysheet.create(this.options)
  359. })
  360. },
  361. // 获取分类
  362. async getClassify() {
  363. let res=await dataInterence.getClassify()
  364. if(res.Ret===200){
  365. this.classifyArray = res.Data.List || []
  366. let item = this.classifyArray.find(item => item.ClassifyId == this.dataForm.firstClassify)
  367. this.firstClassName = item && item.ClassifyName
  368. this.secondClassifyArray = item ? item.Child : []
  369. if(this.secondClassifyArray.length>0){
  370. let item2 = this.secondClassifyArray.find(item => item.ClassifyId == this.dataForm.secondClassify)
  371. this.secondClassName = item2 && item2.ClassifyName
  372. }
  373. }
  374. },
  375. // 获取指标单位
  376. async getTargetUnitList(){
  377. let res=await dataInterence.getTargetUnitList()
  378. if(res.Ret===200){
  379. this.unitList = res.Data || []
  380. }
  381. },
  382. // 切换选择一级分类
  383. changeFirstClassify(id){
  384. let col = this.options.column
  385. let item = this.classifyArray.find(item => item.ClassifyId == id)
  386. this.firstClassName = item.ClassifyName
  387. // 指标分类数组
  388. this.secondClassifyArray = item.Child || []
  389. if(this.secondClassifyArray.length >0){
  390. // 默认选择第一个指标分类
  391. this.dataForm.secondClassify = this.secondClassifyArray[0].ClassifyId
  392. this.secondClassName = this.secondClassifyArray[0].ClassifyName
  393. }
  394. for (let i= 1; i< col+1 ; i++) {
  395. luckysheet.setCellValue(0,i,this.firstClassName,{isRefresh:false})
  396. luckysheet.setCellValue(1,i,this.secondClassName,{isRefresh:i==col?true:false})
  397. }
  398. },
  399. // 切换选择二级分类
  400. changeSecondClassify(id){
  401. let col = this.options.column
  402. let item = this.secondClassifyArray.find(item => item.ClassifyId == id)
  403. for (let i= 1; i< col+1; i++) {
  404. luckysheet.setCellValue(1,i,item.ClassifyName,{isRefresh:i==col?true:false})
  405. }
  406. },
  407. // 切换选择频度
  408. changeFrequency(value){
  409. let col = this.options.column
  410. for (let i= 1; i< col+1; i++) {
  411. luckysheet.setCellValue(2,i,value,{isRefresh:i==col?true:false})
  412. }
  413. },
  414. saveExcel(){
  415. this.$refs.dataOptions.validate((valid)=>{
  416. if(valid){
  417. this.saveLoading=true
  418. let data = luckysheet.getAllSheets()
  419. dataInterence.saveOnlineExcel({...data[0],ClassifyId:this.dataForm.secondClassify}).then(res=>{
  420. if(res.Ret == 200) {
  421. this.setCache('submit')
  422. this.$router.push({path:'/dataList'})
  423. }
  424. }).finally(()=>{
  425. this.saveLoading=false
  426. })
  427. }
  428. })
  429. },
  430. cancel(){
  431. this.setCache()
  432. this.$router.back()
  433. },
  434. // 设置sessionStorage缓存
  435. setCache(type = 'cancel'){
  436. // 提交
  437. let params={}
  438. if(type == 'submit'){
  439. params = this.dataForm
  440. }else{
  441. params = this.copyDataForm
  442. }
  443. let query={
  444. tradeCode:params.tradeCode,
  445. secondClassify:params.secondClassify,
  446. frequency:params.frequency,
  447. firstClassify:params.firstClassify,
  448. tradeName:params.tradeName
  449. }
  450. sessionStorage.setItem('onlineExcelBack',JSON.stringify(query))
  451. }
  452. },
  453. }
  454. </script>
  455. <style lang="scss" scoped>
  456. .excel-data-container{
  457. display: flex;
  458. height: calc(100vh - 110px);
  459. .online-excel{
  460. flex-grow: 1;
  461. height: 100%;
  462. padding: 10px;
  463. border: 1px solid #ECECEC;
  464. box-sizing: border-box;
  465. #luckysheet{
  466. height: 100%;
  467. width: 100%;
  468. margin: 0;
  469. padding: 0;
  470. }
  471. }
  472. .data-options-box{
  473. min-width: 270px;
  474. height: 100%;
  475. margin-left: 20px;
  476. width: 270px;
  477. box-sizing: border-box;
  478. .data-options-buttons{
  479. border: 1px solid #ECECEC;
  480. box-sizing: border-box;
  481. padding: 20px 15px;
  482. }
  483. .data-options{
  484. padding: 25px 15px;
  485. box-sizing: border-box;
  486. .instruction-hint{
  487. display: inline-flex;
  488. align-items: center;
  489. cursor: pointer;
  490. span{
  491. font-size: 16px;
  492. margin-right: 4px;
  493. }
  494. }
  495. }
  496. }
  497. }
  498. .hint-message{
  499. font-size: 14px;
  500. line-height: 22px;
  501. }
  502. </style>
  503. <style lang="scss">
  504. // .online-excel{
  505. // }
  506. .data-options{
  507. .el-form--label-top .el-form-item__label{
  508. line-height: 16px;
  509. color: #333333;
  510. font-size: 16px;
  511. }
  512. }
  513. .luckysheet .toolbar {
  514. background: none;
  515. margin: 0;
  516. padding: 0;
  517. }
  518. </style>