123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702 |
- <template>
- <div class="excel-data-container" v-loading="saveLoading"
- :element-loading-text="$t('Edb.save_ing')" element-loading-spinner="el-icon-loading"
- element-loading-background="rgba(0, 0, 0, 0.8)">
- <div class="online-excel">
- <div id="luckysheet"></div>
- </div>
- <div class="data-options-box">
- <div class="data-options-buttons">
- <el-button type="primary" style="width: 80px;font-size: 16px;" @click="saveExcel"><!-- 保存 -->{{$t('Dialog.confirm_save_btn')}}</el-button>
- <el-button plain style="width: 80px;font-size: 16px;" @click="cancel"><!-- 取消 -->{{$t('Dialog.cancel_btn')}}</el-button>
- </div>
- <div class="data-options" >
- <el-form :model="dataForm" label-position="top" ref="dataOptions">
- <el-form-item :label="$t('ManualEntryPage.label_one_level_classify')" prop="firstClassify" :rules="[{required:true,message:'品种不能为空',trigger:'change'}]">
- <el-select v-model="dataForm.firstClassify" placeholder="请选择品种" style="width: 100%;" @change="changeFirstClassify">
- <el-option :label="item.ClassifyName" :value="item.ClassifyId" v-for="item in classifyArray" :key="item.ClassifyId"></el-option>
- </el-select>
- </el-form-item>
- <el-form-item :label="$t('ManualEntryPage.label_edb_classify')" prop="secondClassify" :rules="[{required:true,message:'子品种不能为空',trigger:'change'}]">
- <el-select v-model="dataForm.secondClassify" placeholder="请选择子品种" style="width: 100%;" @change="changeSecondClassify">
- <el-option :label="item.ClassifyName" :value="item.ClassifyId" v-for="item in secondClassifyArray" :key="item.ClassifyId"></el-option>
- </el-select>
- </el-form-item>
- <el-form-item :label="$t('Edb.Detail.e_fre')" prop="frequency" :rules="[{required:true,message:'频度不能为空',trigger:'change'}]">
- <el-select v-model="dataForm.frequency" placeholder="请选择频度" style="width: 100%;" @change="changeFrequency">
- <el-option :label="item.label" :value="item.value" v-for="item in frequencyList" :key="item.value"></el-option>
- </el-select>
- </el-form-item>
- </el-form>
- <el-tooltip :open-delay="400">
- <div slot="content" v-html="$t('ManualEntryPage.use_msg')">
-
- </div>
- <div class="instruction-hint" >
- <span><!-- 使用说明 -->{{$t('ManualEntryPage.use_intro')}}</span>
- <i class="el-icon-question" style="color: #999;"></i>
- </div>
- </el-tooltip>
- </div>
- </div>
- </div>
- </template>
- <script>
- import { dataInterence } from "@/api/api.js";
- import EventBus from '@/api/bus.js';
- import {frequencySelectList} from '@/utils/defaultOptions';
- export default {
- name:'onlineExcelCopy',
- data() {
- // 受保护的数据 维护一个二维数组 用于还原不能被修改或者以错误方式修改的数据
- this.protectedData=[]
- // 初始化数据
- this.options={
- container:'luckysheet',
- lang: this.$i18nt.locale,
- gridKey:'handmade-excel',
- showinfobar:false,
- row:300,
- column:60,
- data:[
- {
- "name":"test",
- "celldata":[
- {
- "r":0,
- "c":0,
- "v":{
- ct: {fa: "@", t: "s"},
- m:"一级分类",
- v:"一级分类",
- bg:'#b4a7d6'
- },
- },
- {
- "r":1,
- "c":0,
- "v":{
- ct: {fa: "@", t: "s"},
- m:"指标分类",
- v:"指标分类",
- bg:'#b4a7d6'
- },
- },
- {
- "r":2,
- "c":0,
- "v":{
- ct: {fa: "@", t: "s"},
- m:"频度",
- v:"频度",
- bg:'#b4a7d6'
- },
- },
- {
- "r":3,
- "c":0,
- "v":{
- ct: {fa: "@", t: "s"},
- m:"指标名称",
- v:"指标名称",
- bg:'#b4a7d6'
- },
- },
- {
- "r":4,
- "c":0,
- "v":{
- ct: {fa: "@", t: "s"},
- m:"单位",
- v:"单位",
- bg:'#b4a7d6'
- },
- },
- ],
- "config":{
- "borderInfo":[{
- "rangeType": "range",
- "borderType": "border-all",
- "style": "1",
- "color": "#fff",
- "range": [{
- "row": [0,4],
- "column": [0,0]
- }]
- }],
- },
- "frozen":{
- type: 'rangeBoth',
- range: {row_focus: 4, column_focus: 0}
- },
- "dataVerification":{}
- }
- ],
- hook:{},
- showsheetbar:false
- }
- return {
- dataForm:{
- firstClassify:'',
- secondClassify:'',
- frequency:'',
- tradeCode:''
- },
- copyDataForm:{},
- firstClassName:'',
- secondClassName:'',
- //frequencyList: ['日度','周度','旬度','月度','季度','半年度','年度'],
- classifyArray:[],
- unitList:[],
- secondClassifyArray:[],
- // 保存时候的加载框
- saveLoading:false,
- isEdit:"false",
- refreshed:false,
- haveSaveProtected:false,
- hintMsg:`
- 1、新增指标:在预设好的指标列下输入指标名称、单位、数值并保存;</br>
- 2、新增日期:在第一列选择日期或插入日期行(需复制已有日期格式)输入数值并保存;</br>
- 3、清除指标:清空该指标下所有数据并保存,该操作不会删除指标,仅清除指标数据,请勿直接删除指标列;</br>
- 4、删除日期:清空该日期对应所有数据并保存,请勿直接删除日期行;</br>
- 5、指标名称编辑、单位编辑、频度编辑、指标删除等操作请在“手工指标列表”中完成;</br>
- `
- }
- },
- computed:{
- frequencyList(){
- return frequencySelectList()
- }
- },
- created() {
- // this.dataForm.firstClassify = parseInt(this.$route.query.parentClassifyId) || ''
- // this.dataForm.secondClassify = parseInt(this.$route.query.ClassifyId) || ''
- // this.dataForm.frequency = this.$route.query.Frequency || ''
- // this.dataForm.tradeCode = this.$route.query.TradeCode || ''
- // this.dataForm.tradeName = this.$route.query.TradeName || ''
- // this.copyDataForm = JSON.parse(JSON.stringify(this.dataForm))
- // // 字符串
- // this.isEdit = this.$route.query.isEdit.toString()
- this.initExcelOptions()
- // 监听折叠导航栏事件
- EventBus.$off('collapseHandle')
- EventBus.$on('collapseHandle',(params)=>{
- luckysheet.destroy()
- luckysheet.create(this.options)
- })
- },
- beforeRouteLeave(to, from, next) {
- luckysheet.destroy()
- next();
- },
- // 监听浏览器返回事件
- mounted() {
- window.addEventListener('popstate',this.setCache)
- },
- unmounted() {
- window.removeEventListener('popstate',this.setCache)
- },
- methods: {
- async initExcelOptions(){
- // 获取单位接口
- await this.getTargetUnitList()
- // 获取分类
- await this.getClassify()
- let dataList = []
- // 编辑才请求数据
- if(this.dataForm.secondClassify && this.isEdit == "true"){
- let res = await dataInterence.getTargetList({
- Frequency: this.dataForm.frequency,
- ClassifyId: this.dataForm.secondClassify,
- TradeCode:this.dataForm.tradeCode,
- EdbShowType: this.dataForm.tradeCode?0:1,
- })
- if(res.Ret == 200){
- dataList = res.Data.List || []
- }
- }
- // hook设置
- // 处理复制粘贴造成的漏洞 - 记录数据
- this.options.hook.cellAllRenderBefore=(data,sheet)=>{
- if(this.haveSaveProtected) return
- if(!this.protectedData.length>0){
- for (let i = 0; i < 5; i++) {
- if([0,1,2].includes(i)){
- for (let j = 0; j < 2; j++) {
- const element = data[i][j];
- if(!Array.isArray(this.protectedData[i])) this.protectedData[i]=[]
- this.protectedData[i][j] = element
- }
- }else{
- for (let k = 0; k <= dataList.length; k++) {
- const element = data[i][k];
- if(!Array.isArray(this.protectedData[i])) this.protectedData[i]=[]
- this.protectedData[i][k] = element
- }
- }
- }
- }
- this.haveSaveProtected=true
- }
- this.options.hook.cellUpdateBefore=(r,c)=>{
- let edbNameEle = luckysheet.getSheetData()[3][c]
- // 禁止更新的单元格 已经有的指标名称和单位不允许更新
- if(([0,1,2].includes(r) && c==0)
- || ([3,4].includes(r) && edbNameEle && this.protectedData[3].some(data => data.v == edbNameEle.v))){
- return false
- }
- }
- this.options.hook.cellMousedownBefore=(e,position)=>{
- let edbNameEle = luckysheet.getSheetData()[3][position.c]
- // 禁止鼠标选中的单元格
- if([0,1,2].includes(position.r)
- || ([3,4].includes(position.r) && edbNameEle && this.protectedData[3].some(data => data.v == edbNameEle.v))){
- return false
- }
- }
- // 处理复制粘贴造成的漏洞 - 还原数据 PS:这个钩子函数只有在单元格在可是区域内才会执行
- this.options.hook.cellRenderBefore=(cell,position,sheet,ctx)=>{
- // 清除两边空格
- if(cell && cell.m && typeof(cell.m)=='string'){
- cell.m = cell.m.trim()
- }
- if(cell && cell.v && typeof(cell.v)=='string'){
- cell.v = cell.v.trim()
- }
- /**
- * 从excel整个单元格复制,到这里进入编辑模式的粘贴,出来个cell.ct.s,不知道啥东西,官网也没写
- * 看似有内容,实则保存没内容,因为cell.m和cell.v 都是空。直接清掉了
- */
- if(cell && cell.ct && cell.ct.s){
- cell.ct.s=''
- }
-
- // 是否更改了受保护的数据
- let haveUpdatedProtectedData=false
- if([3,4].includes(position.r)){
- if(!cell) return
- let index = this.protectedData[3].findIndex((pd,ind) => pd && ind==position.c)
- if(index ==-1) return
- Object.keys(this.protectedData[position.r][index]).forEach(key => {
- if(!_.isEqual(cell[key], this.protectedData[position.r][index][key])){
- haveUpdatedProtectedData=true
- cell[key] = this.protectedData[position.r][index][key]
- }
- });
- }else if([0,1,2].includes(position.r)){
- let cindex = position.c>1?1:position.c
- if(!cell){
- if(cindex<1){
- return
- }
- sheet.data[position.r][position.c] = this.protectedData[position.r][1]
- }else{
- Object.keys(this.protectedData[position.r][cindex]).forEach(key => {
- if(!_.isEqual(cell[key], this.protectedData[position.r][cindex][key])){
- haveUpdatedProtectedData=true
- cell[key] = this.protectedData[position.r][cindex][key]
- }
- });
- if(position.c>0){
- if(position.r==0){
- if(cell.m !==this.firstClassName || cell.v !== this.firstClassName){
- haveUpdatedProtectedData=true
- cell.m = cell.v = this.firstClassName
- }
- }else if(position.r==1){
- if(cell.m !==this.secondClassName || cell.v !== this.secondClassName){
- haveUpdatedProtectedData=true
- cell.m = cell.v = this.secondClassName
- }
- }else{
- if(cell.m !==this.dataForm.frequency || cell.v !== this.dataForm.frequency){
- haveUpdatedProtectedData=true
- cell.m = cell.v = this.dataForm.frequency
- }
- }
- }
- }
- }
- if(haveUpdatedProtectedData){
- // 还原数据后的手动刷新
- this.refreshManual()
- }
- }
- this.options.hook.updated=(operation)=>{
- // 维护 protectedData数据
- if(operation.type == 'delRC'){
- // 删除列
- if(operation.ctrlValue.rc == 'c'){
- // console.log('删除列',`第${operation.ctrlValue.index+1}列开始`,`删除了${operation.ctrlValue.len}列`);
- if(!operation.data){
- // 没有data为撤销 恢复他
- for (let i = operation.ctrlValue.index; i < operation.ctrlValue.index+operation.ctrlValue.len; i++) {
- // 在celldata初始数据里面找,有找到说明不是新加的
- let isFind = luckysheet.getSheet().celldata.some(cd => {
- return cd.r==3 && cd.c < operation.ctrlValue.index+operation.ctrlValue.len+1 &&
- cd.v && cd.v.v == operation.curdata[3][i].v
- })
- this.protectedData[3].splice(i,0,isFind?operation.curdata[3][i]:false)
- this.protectedData[4].splice(i,0,isFind?operation.curdata[4][i]:false)
- }
- }else{
- for (let i = operation.ctrlValue.index; i < operation.ctrlValue.index+operation.ctrlValue.len; i++) {
- if(operation.ctrlValue.index > this.protectedData[3].length){
- break;
- }
- this.protectedData[3].splice(operation.ctrlValue.index,1)
- this.protectedData[4].splice(operation.ctrlValue.index,1)
- }
- }
- }
- }else if(operation.type == 'addRC' && operation.ctrlValue.rc == 'c'){
- // 增加列
- let addIndex = operation.ctrlValue.index
- if(operation.ctrlValue.direction=='lefttop'){
- // 向左加
- addIndex--
- }
- // 比原本的protectedData数组长度大,就不会对protectedData造成形象 就return
- if(!(addIndex < this.protectedData[3].length)) return
- // console.log('增加列',`第${addIndex+1}列开始向右`,`增加了${operation.ctrlValue.len}列,${operation.data?"正向操作":"撤销"}`);
- if(!operation.data){
- // 没有data为撤销 删除它
- for (let i = addIndex; i < addIndex+operation.ctrlValue.len; i++) {
- this.protectedData[3].splice(addIndex+1,1)
- this.protectedData[4].splice(addIndex+1,1)
- }
- }else{
- for (let i = addIndex; i < addIndex+operation.ctrlValue.len; i++) {
- // 新加的列,只占个位置,保持索引相对,不需要保护,置为false
- this.protectedData[3].splice(i+1,0,false)
- this.protectedData[4].splice(i+1,0,false)
- }
- }
- }
- }
- // 拿出所有数组
- let arr = dataList.map((item => item.DataList)).flat()
- // 拿出所有数组的日期并展平排序
- let dateArr = [...new Set(arr.map((item) => item.Dt))].sort().reverse()
- // 组合成需要的数据格式
- let excelData=dateArr.map(date =>{
- let objList = [date]
- dataList.forEach(element => {
- let obj = element.DataList.find((time) => time.Dt === date)
- objList.push(obj?obj.Close : '')
- });
- return objList
- })
- /**
- * 当数据的长度大于默认的300时,行数需要设置成数据的长度+15
- */
- this.options.row = dateArr.length>(this.options.row-15) ? (dateArr.length+15):this.options.row
- /**
- * 设置列数
- * 当数据的长度大于默认的60时,列数需要设置成数据的长度+10
- */
- this.options.column = dataList.length>(this.options.column-10) ? (dataList.length+10):this.options.column
- let row = this.options.row
- let col = this.options.column
- // 初始化数据
- dataList.map((item,index) =>{
- this.options.data[0].celldata.push(
- {
- "r":3,
- "c":1+index,
- "v":{
- ct: {fa: "@", t: "s"},
- m:item.SecName,
- v:item.SecName,
- bg:'#cccccc'
- }
- },
- {
- "r":4,
- "c":1+index,
- "v":{
- ct: {fa: "@", t: "s"},
- m:item.Unit,
- v:item.Unit,
- bg:'#cccccc'
- }
- })
- })
- for (let i = 0; i < col; i++) {
- this.options.data[0].celldata.push(
- {
- "r":0,
- "c":1+i,
- "v":{
- ct: {fa: "@", t: "s"},
- m:this.firstClassName || '',
- v:this.firstClassName || '',
- bg:'#cccccc'
- }
- },
- {
- "r":1,
- "c":1+i,
- "v":{
- ct: {fa: "@", t: "s"},
- m:this.secondClassName || '',
- v:this.secondClassName || '',
- bg:'#cccccc'
- }
- },
- {
- "r":2,
- "c":1+i,
- "v":{
- ct: {fa: "@", t: "s"},
- m:this.dataForm.frequency || '',
- v:this.dataForm.frequency || '',
- bg:'#cccccc'
- }
- })
- }
- for (let i = 0; i < row-5; i++) {
- for (let j = 0; j < (col+1); j++) {
- this.options.data[0].celldata.push(
- {
- "r":5+i,
- "c":0+j,
- "v":{
- ct: j==0?{fa: "yyyy-MM-dd", t: "d"}:{fa: "0.0000", t: "n"},
- m:excelData[i]?excelData[i][j]:'',
- v:excelData[i]?excelData[i][j]:'',
- }
- })
- }
- }
- // // 设置日期单元格颜色
- this.options.data[0].celldata.forEach(element => {
- if(element.c==0 && (![0,1,2,3,4].includes(element.r))){
- element.v.bg = '#ff9900'
- }
- });
- // 设置边框颜色
- this.options.data[0].config.borderInfo.push({
- "rangeType": "range",
- "borderType": "border-all",
- "style": "1",
- "color": "#fff",
- "range": [{
- "row": [0,2],
- "column": [1,col]
- }]
- },{
- "rangeType": "range",
- "borderType": "border-all",
- "style": "1",
- "color": "#fff",
- "range": [{
- "row": [5,row],
- "column": [0,0]
- }]
- },{
- "rangeType": "range",
- "borderType": "border-all",
- "style": "1",
- "color": "#fff",
- "range": [{
- "row": [3,4],
- "column": [1,dataList.length]
- }]
- })
- // 设置数据验证
- for (let i = 0; i < col; i++) {
- this.options.data[0].dataVerification[`4_${1+i}`]={
- "type": "dropdown",
- "type2": null,
- "value1": this.unitList.join(','),
- "value2": "",
- "prohibitInput": false,
- }
- }
- this.$nextTick(()=>{
- luckysheet.create(this.options)
- })
- },
- refreshManual: _.debounce(function() {
- if(!this.refreshed) {
- luckysheet.refresh()
- this.refreshed=true
- setTimeout(()=>{
- this.refreshed=false
- },1500)
- }
- },1000),
- // 获取分类
- async getClassify() {
- let res=await dataInterence.getClassify()
- if(res.Ret===200){
- this.classifyArray = res.Data.List || []
-
- this.dataForm.firstClassify = this.classifyArray[0].ClassifyId;
- this.firstClassName = this.classifyArray[0].ClassifyName
- this.secondClassifyArray = this.classifyArray[0] ? this.classifyArray[0].Child : []
- if(this.secondClassifyArray.length>0){
- this.dataForm.secondClassify = this.secondClassifyArray[0].ClassifyId;
- this.secondClassName = this.secondClassifyArray[0].ClassifyName
- }
- }
- },
- // 获取指标单位
- async getTargetUnitList(){
- let res=await dataInterence.getTargetUnitList()
- if(res.Ret===200){
- this.unitList = res.Data || []
- }
- },
- // 切换选择一级分类
- changeFirstClassify(id){
- let col = this.options.column
- let item = this.classifyArray.find(item => item.ClassifyId == id)
- this.firstClassName = item.ClassifyName
- // 指标分类数组
- this.secondClassifyArray = item.Child || []
- if(this.secondClassifyArray.length >0){
- // 默认选择第一个指标分类
- this.dataForm.secondClassify = this.secondClassifyArray[0].ClassifyId
- this.secondClassName = this.secondClassifyArray[0].ClassifyName
- }
- luckysheet.refresh()
- },
- // 切换选择二级分类
- changeSecondClassify(id){
- let col = this.options.column
- let item = this.secondClassifyArray.find(item => item.ClassifyId == id)
- this.secondClassName=item.ClassifyName
- luckysheet.refresh()
- },
- // 切换选择频度
- changeFrequency(value){
- luckysheet.refresh()
- },
- saveExcel(){
- this.$refs.dataOptions.validate((valid)=>{
- if(valid){
- this.saveLoading=true
- let data = luckysheet.getAllSheets()
- for (let i = 0; i < data[0].data.length; i++) {
- // 清除日期列的两边的空格,防止保存时日期变成0000-00-00
- const element = data[0].data[i][0];
- if(element && element.v && typeof(element.v)=='string'){
- element.v = element.v.trim()
- }
- if(element && element.m && typeof(element.m)=='string'){
- element.m = element.m.trim()
- }
- }
- dataInterence.saveOnlineExcel({...data[0],ClassifyId:this.dataForm.secondClassify}).then(res=>{
- if(res.Ret == 200) {
- this.setCache('submit')
- this.$router.push({path:'/dataList'})
- }
- }).finally(()=>{
- this.saveLoading=false
- })
- }
- })
- },
- cancel(){
- this.setCache()
- this.$router.back()
- },
- // 设置sessionStorage缓存
- setCache(type = 'cancel'){
- // 提交
- let params={}
- if(type == 'submit'){
- params = this.dataForm
- }else{
- params = this.copyDataForm
- }
- let query={
- tradeCode:params.tradeCode,
- secondClassify:params.secondClassify,
- frequency:params.frequency,
- firstClassify:params.firstClassify,
- tradeName:params.tradeName
- }
- sessionStorage.setItem('onlineExcelBack',JSON.stringify(query))
- }
- },
- }
- </script>
- <style lang="scss" scoped>
- .excel-data-container{
- display: flex;
- height: calc(100vh - 120px);
- .online-excel{
- flex-grow: 1;
- height: 100%;
- padding: 10px;
- border: 1px solid #ECECEC;
- box-sizing: border-box;
- #luckysheet{
- height: 100%;
- width: 100%;
- margin: 0;
- padding: 0;
- }
- }
- .data-options-box{
- min-width: 270px;
- height: 100%;
- margin-left: 20px;
- width: 270px;
- box-sizing: border-box;
- .data-options-buttons{
- border: 1px solid #ECECEC;
- box-sizing: border-box;
- padding: 20px 15px;
- }
- .data-options{
- padding: 25px 15px;
- box-sizing: border-box;
- .instruction-hint{
- display: inline-flex;
- align-items: center;
- cursor: pointer;
- span{
- font-size: 16px;
- margin-right: 4px;
- }
- }
- }
- }
- }
- .hint-message{
- font-size: 14px;
- line-height: 22px;
- }
- </style>
- <style lang="scss">
- // .online-excel{
- // }
- .data-options{
- .el-form--label-top .el-form-item__label{
- line-height: 16px;
- color: #333333;
- font-size: 16px;
- }
- }
- .luckysheet .toolbar {
- background: none;
- margin: 0;
- padding: 0;
- }
- </style>
|