Browse Source

混合/时间序列/共享表格搭建完成

chenlei 5 months ago
parent
commit
8c12907817

+ 8 - 2
src/api/index.js

@@ -18,6 +18,10 @@ const LOADINGWHITELIST=[
   '/datamanage/edb_info/refresh',
   '/datamanage/edb_info/refresh/all'
 ]
+//l 下载请求列表
+const DownloadRequestslist = [
+  '/resource/file/download',
+]
 // 请求数
 let LOADINGCOUNT = 0;
 let LOADING;
@@ -45,6 +49,7 @@ _axios.interceptors.request.use(
     }
     
     config.headers.Authorization=localStorage.getItem('token')||''
+    if(DownloadRequestslist.includes(config.url)) config.responseType = 'blob'
     return config;
   },
   function (error) {
@@ -58,12 +63,13 @@ _axios.interceptors.response.use(
   function (response) {
     // Do something with response data
     let data
-    if(import.meta.env.MODE==='production'){
+    if(import.meta.env.MODE==='production' && !DownloadRequestslist.includes(config.url)){  // 生产环境解密 --下载数据请求不需要解密
       const headKeyStr=response.headers.dk
       const desKey=CryptoJS.Des3Decrypt(headKeyStr,'JMCqSoUrTAmyNNIRb0TtlrPk') 
       data=JSON.parse(CryptoJS.Des3Decrypt(response.data,desKey));//解密
     }else{
-      data=response.data
+      if(DownloadRequestslist.includes(config.url)) data = response
+      else data=response.data
     }
     //关闭loading
     if(!LOADINGWHITELIST.includes(response.config.url)){

+ 44 - 2
src/api/sheet.js

@@ -45,7 +45,7 @@ export default {
      * 表格分类列表
      * @param Source 表格类型
      * @param IsShowMe 是否只显示我创建的
-     */
+    */
        excelClassifyList(params){
         return get('/datamanage/excel_classify/list',params)
     },
@@ -53,8 +53,50 @@ export default {
     /**
      * 表格详情
      * @param ExcelInfoId 表格ID
-     */
+    */
     excelDetail(params){
         return get('/datamanage/excel_info/detail',params)
     },
+
+    /**
+     * 删除表格
+     * @param ExcelClassifyId 表格分类ID
+     * @param ExcelInfoId 表格ID
+     * @param Source 表格来源
+    */
+    excelDelete(params){
+        return post('/datamanage/excel_classify/delete',params)
+    },
+    
+    /**
+     *  下载文件公共接口
+    */
+    apiDownloadResource(params){
+        return get('/resource/file/download',params)
+    },
+
+    /**
+     * 获取平衡表图表数据
+     * @param ExcelInfoId 表格ID
+    */
+    getBalanceChartData(params){
+        return get('/datamanage/excel_info/balance/chart_list',params)
+    },
+
+    /**
+     * 平衡表获取版本列表
+     * @param ExcelInfoId 表格ID
+    */
+    balanceTableVersion(params){
+        return get('/datamanage/excel_info/balance/version',params)
+    },
+
+    /**
+     * 平衡表获取子表列表
+     * @param ParentId 表格ID
+    */
+    getBalanceChildTable(params){
+        return get('/datamanage/excel_info/child_table',params)
+    },
+    
 }

+ 24 - 0
src/lang/cn.js

@@ -4,6 +4,13 @@ export default {
     home: '首页',
     my: '我的',
   },
+  // 我的
+  my: {
+    company_name: '公司名称',
+    department: '所属部门',
+    role: '角色',
+    log_out: '退出登录',
+  },
   // 表格tabbar
   tableTabbar: {
     shared_table: '共享表格',
@@ -19,5 +26,22 @@ export default {
     no_table: '暂无表格',
     no_more: '没有更多了',
     choose_category: '选择分类',
+    author: '作者',
+    recent_save_time: '最近保存时间',
+    add_my_gallery: '加入我的图库',
+    added_my_successfully: '加入我的图库成功',
+    close: '关闭',
+    add_category: '添加分类',
+    confirm: '确定',
+    please_enter_category: '请输入分类名称',
+    cancel: '取消',
+    refresh: '刷新',
+    download: '下载',
+    delete: '删除',
+    note: '提示',
+    once_deleted: '删除后表格将不能再引用,确认删除吗?',
+    deletion_success: '删除成功',
+    refresh_success: '刷新成功',
+    download_success: '下载成功',
   },
 }

+ 24 - 0
src/lang/en.js

@@ -2,6 +2,13 @@ export default {
   tabbar: {
     home: 'home',
     my: 'my',
+  },
+  // 我的
+  my: {
+    company_name: 'Company Name',
+    department: 'Department',
+    role: 'Role',
+    log_out: 'Log out',
   },
    // 表格tabbar
    tableTabbar: {
@@ -18,5 +25,22 @@ export default {
     no_table: 'No table available',
     no_more: 'No more',
     choose_category: 'Choose a category',
+    author: 'Author',
+    recent_save_time: 'Recent save time',
+    add_my_gallery: 'Add to my gallery',
+    added_my_successfully: 'Added to my gallery successfully',
+    close: 'Close',
+    add_category: 'Add Category',
+    confirm: 'Confirm',
+    please_enter_category: 'Please enter the category name',
+    cancel: 'Cancel',
+    refresh: 'Refresh',
+    download: 'Download',
+    delete: 'Delete',
+    note: 'Note',
+    once_deleted: 'Once deleted, this table will no longer be referable. Are you sure you want to delete it?',
+    deletion_success: 'Deletion Successful',
+    refresh_success: 'Refresh Successful',
+    download_success: 'Download Successful',
   },
 }

+ 24 - 12
src/router/sheetList.js

@@ -12,18 +12,6 @@ export const sheetListRoutes=[
                 component: () => import("@/views/sheetList/sharedList.vue"),
                 meta: { title: "共享表格",noBack:true },
             },
-            {
-                path: "/shared/search",
-                name: "sharedSearch",
-                component: () => import("@/views/sheetList/sharedSearch.vue"),
-                meta: { title: "共享表格",noBack:true },
-            },
-            {
-                path: "/shared/detail",
-                name: "sharedDetail",
-                component: () => import("@/views/sheetList/sharedDetail.vue"),
-                meta: { title: "共享表格",noBack:true },
-            },
             {
                 path: "/timeline/list",
                 name: "timelineList",
@@ -47,4 +35,28 @@ export const sheetListRoutes=[
             title: "ETA表格",
         },
     },
+    {
+        path: "/shared/search",
+        name: "sharedSearch",
+        component: () => import("@/views/sheetList/sharedSearch.vue"),
+        meta: { title: "共享表格" },
+    },
+    {
+        path: "/shared/detail",
+        name: "sharedDetail",
+        component: () => import("@/views/sheetList/sharedDetail.vue"),
+        meta: { title: "共享表格" },
+    },
+    {
+        path: "/balance/detail",
+        name: "balanceDetail",
+        component: () => import("@/views/sheetList/balanceDetail.vue"),
+        meta: { title: "共享表格" },
+    },
+    {
+        path: "/balance/chart",
+        name: "balanceChart",
+        component: () => import("@/views/sheetList/balanceChart.vue"),
+        meta: { title: "共享表格" },
+    }
 ]

+ 80 - 0
src/views/sheetList/balanceChart.vue

@@ -0,0 +1,80 @@
+<script setup name="ChartETAChartDetail">
+import AddChartToMyETA from './components/AddChartToMyETA.vue';
+import { useRoute, useRouter } from "vue-router";
+import { ref, onMounted } from 'vue';
+import apiSheet from '@/api/sheet'
+import { useWindowSize } from '@vueuse/core'
+const chartList = ref([]);
+const isShowAddToMyETADialog = ref(false);
+const route = useRoute()
+const { width } = useWindowSize()
+
+onMounted(() => {
+    // getChartList('init')
+})
+
+// 获取图表数据
+async function getChartList(){
+    const res = await apiSheet.getBalanceChartData({
+            ExcelInfoId: Number(route.query.id)
+        })
+    if(res.Ret!==200) return 
+    chartList.value = res.Data.List||[]
+}
+</script>
+
+<template>
+    <div class="sheet-chart-page" v-if="chartList">
+        <div class="sheet-name">表格名称sheet/版本</div>
+        <!-- 图渲染区域 -->
+       <div class="chart-box">
+            <div class="chart-name">图表名称</div>
+            <div class="chart"></div>
+            <div class="add-chart" @click="isShowAddToMyETADialog = true">加入我的图库</div>
+       </div>
+        <!-- 加入我的图库弹窗 -->
+        <AddChartToMyETA 
+            :isShowDialog="isShowAddToMyETADialog"
+            :dialogPosition="width>650?'center':'bottom'"
+            :chartInfo="chartList"
+            @close="isShowAddToMyETADialog=false"
+        />
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.sheet-chart-page {
+    padding: 20px;
+    .sheet-name {
+        margin: 0 0 20px 0;
+    }
+    .chart-box {
+        width: 100%;
+        height: 50vh;
+        padding: 20px;
+        border: 1px solid rgba(0, 0, 0, 0.38);
+        .chart-name {
+            margin: 0 0 20px 0;
+            line-height: 30px;
+        }
+        .chart {
+            width: 100%;
+            height: calc(100% - 110px);
+            background-color: pink;
+        }
+        .add-chart {
+            margin-top: 20px;
+            width: 100%;
+            height: 40px;
+            text-align: right;
+            color:  rgba(54, 121, 224, 1);
+        }
+    }
+    @media screen and (min-width: 650px) {
+        .chart-box {
+            height: 500px;
+        }
+    }
+}
+</style>
+

+ 410 - 0
src/views/sheetList/balanceDetail.vue

@@ -0,0 +1,410 @@
+<script setup name="sharedDetail">
+import {nextTick, onMounted, ref, computed, getCurrentInstance} from 'vue'
+import { useRoute, useRouter } from "vue-router";
+import apiSheet from '@/api/sheet'
+import { useWindowSize } from '@vueuse/core'
+import { showToast, showDialog  } from "vant";
+import {usePublicSettingStore} from '@/store/modules/publicSetting'
+const publicSettingStore = usePublicSettingStore()
+// import { useDownLoadFile } from '@/hooks/useDownLoadFile'
+const { appContext : { config: { globalProperties } } } = getCurrentInstance();
+const { width } = useWindowSize()
+const route = useRoute()
+const router = useRouter()
+const queryData = ref({})
+const link = ref(publicSettingStore.publicSetting.ChartViewUrl);
+//显示更多操作栏
+let showMoreAction = ref(false)
+let showPicker = ref(false)
+let showVersionPicker = ref(false)
+const sheetActions = computed(() => {
+    return [
+        { label: globalProperties.$t('shared_table.refresh'), types:'flushed'},
+        { label: globalProperties.$t('shared_table.download'), types:'download'},
+        { label: globalProperties.$t('shared_table.delete'), types:'delete'},
+        { label: globalProperties.$t('shared_table.cancel'), types:'cancel'},
+    ]
+})
+
+const downExcelFileUrl = computed(() => {
+    let url = `${
+    import.meta.env.VITE_APP_API_URL
+    }/datamanage/excel_info/table/download?${localStorage.getItem("token")}`;
+    return url;
+})
+
+onMounted(() => {
+    getExcelDetail( 'load' )
+    getVersionList()
+    getChildTable()
+})
+//获取表格列表
+async function getExcelDetail( source = 'refresh', ExcelInfoId = '' ){
+    const detailReq = {
+        ExcelInfoId: route.query.id,
+    }
+    if(source==='delete') detailReq.ExcelInfoId = ExcelInfoId
+    const res = await apiSheet.excelDetail(detailReq)
+    if(res.Ret!==200) return 
+    if(source==='refresh') showToast({ type: 'success', message: globalProperties.$t('shared_table.refresh_success') })
+    queryData.value = res.Data
+}
+
+//更多操作
+function handleActionClick(item){
+    switch(item.types){
+        case 'flushed':
+            getExcelDetail( 'refresh' )
+            showMoreAction.value = false
+            break;
+        case 'download':
+            downloadExcel(queryData.value)
+            showMoreAction.value = false
+            break;
+        case 'delete':
+            showDialog({
+                title: globalProperties.$t('shared_table.note'),
+                message: globalProperties.$t('shared_table.once_deleted'),
+                showCancelButton: true
+            }).then(() => {
+                apiSheet.excelDelete({
+                    ExcelClassifyId: queryData.value.ExcelClassifyId,
+                    ExcelInfoId: queryData.value.ExcelInfoId,
+                    Source: queryData.Source,
+                }).then(res=>{
+                    if(res.Ret!==200) return 
+                    showToast({message: globalProperties.$t('shared_table.deletion_success'),type:'success'})
+                    showMoreAction.value = false
+                    getExcelDetail('delete', res.Data.ExcelInfoId)
+                })
+            }).catch(()=>{})
+            break;
+        case 'cancel':
+            showMoreAction.value = false
+            break;
+    }
+}
+
+/* 下载数据 */
+function downloadExcel(cell) {
+    const { Source, FileUrl, ExcelInfoId, ExcelName } = cell;
+    console.log(cell);
+    
+    let value = "";
+    if (Source === 1) {
+        handleDownloadResource(FileUrl, ExcelName)
+    } else if ([2, 3,5].includes(Source)) {
+        value = `${downExcelFileUrl.value}&ExcelInfoId=${ExcelInfoId}`;
+        console.log(value);
+        
+        const a = document.createElement("a");
+        a.href = value;
+        a.target = "_blank";
+        a.download = ExcelName;
+        a.style.display = "none";
+        document.body.append(a);
+        a.click();
+    }
+}
+// 下载文件
+function handleDownloadResource(url,fileName,successCb,faileCb){
+    const b = new Base64() 
+    const arr = url.split('/')
+    const _fileName = arr[arr.length-1]
+    const fileNameTypeArr = fileName.split('.')
+    const _fileNameTypeArr = _fileName.split('.')
+    const fileNameType = fileNameTypeArr.length > 1 ? fileNameTypeArr[fileNameTypeArr.length-1] : ''
+    const _fileNameType = _fileNameTypeArr.length > 1 ? _fileNameTypeArr[_fileNameTypeArr.length-1] : ''
+    apiSheet.apiDownloadResource({
+        FileName:/* fileName||_fileName */'',
+        FileUrl:b.encode(url) 
+    }).then(res=>{
+        //bus.$parseData(response);
+        const content = res
+        const blob = new Blob([content])
+        console.log(blob);
+        
+        if ('download' in document.createElement('a')) {
+            const elink = document.createElement('a')
+            elink.download = fileNameType?fileName:(fileName+'.'+_fileNameType)
+            elink.style.display = 'none'
+            elink.href = window.URL.createObjectURL(blob)
+            document.body.appendChild(elink)
+            elink.click()
+            window.URL.revokeObjectURL(elink.href)
+            document.body.removeChild(elink)
+        } else {
+            navigator.msSaveBlob(blob, fileNameType?fileName:(fileName+'.'+_fileNameType))
+        }
+        successCb&&successCb()
+    }).catch(()=>{
+        showToast({message: '下载失败',type:'warning'})
+        faileCb&&faileCb()
+    })
+}
+
+function Base64() {
+    // private property
+    var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+    // public method for encoding
+    this.encode = function (input) {
+        var output = "";
+        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+        var i = 0;
+        input = this._utf8_encode(input);
+        while (i < input.length) {
+            chr1 = input.charCodeAt(i++);
+            chr2 = input.charCodeAt(i++);
+            chr3 = input.charCodeAt(i++);
+            enc1 = chr1 >> 2;
+            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+            enc4 = chr3 & 63;
+            if (isNaN(chr2)) {
+                enc3 = enc4 = 64;
+            } else if (isNaN(chr3)) {
+                enc4 = 64;
+            }
+            output = output +
+                _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
+                _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
+        }
+        return output;
+    }
+    // private method for UTF-8 encoding
+    this._utf8_encode = function (string) {
+        string = string.replace(/\r\n/g, "\n");
+        var utftext = "";
+        for (var n = 0; n < string.length; n++) {
+            var c = string.charCodeAt(n);
+            if (c < 128) {
+                utftext += String.fromCharCode(c);
+            } else if ((c > 127) && (c < 2048)) {
+                utftext += String.fromCharCode((c >> 6) | 192);
+                utftext += String.fromCharCode((c & 63) | 128);
+            } else {
+                utftext += String.fromCharCode((c >> 12) | 224);
+                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+                utftext += String.fromCharCode((c & 63) | 128);
+            }
+        }
+        return utftext;
+    }
+}
+
+const columns = [
+    { text: '杭州', value: 'Hangzhou' },
+    { text: '宁波', value: 'Ningbo' },
+    { text: '温州', value: 'Wenzhou' },
+    { text: '绍兴', value: 'Shaoxing' },
+    { text: '湖州', value: 'Huzhou' },
+];
+const versionOpts = ref([])
+const childTableOpts = ref([])
+const fieldValue = ref('平衡表');
+const fieldValueVersion = ref('平衡表');
+
+// 平衡表获取版本列表
+function getVersionList(){
+    apiSheet.balanceTableVersion({ExcelInfoId: route.query.id,}).then(res=>{
+        if(res.Ret === 200){
+            const arr = res.Data.List || []
+            versionOpts.value = arr
+        }
+    })
+}
+
+// 平衡表获取子表列表
+function getChildTable(){
+    apiSheet.getBalanceChildTable({ParentId: route.query.id,}).then(res=>{
+        if(res.Ret === 200){
+            const arr = res.Data.List || []
+            childTableOpts.value = arr
+        }
+    })
+}
+
+// 平衡表选择器
+const onConfirm = ({ selectedOptions }) => {
+    showPicker.value = false;
+    fieldValue.value = selectedOptions[0].text;
+};
+
+// 版本选择器
+const onConfirmVersion = ({ selectedOptions }) => {
+    showVersionPicker.value = false;
+    fieldValueVersion.value = selectedOptions[0].text;
+};
+
+// 关联图表页
+function goChart () {
+    router.push({ path: '/balance/chart', query: { id: queryData.value.ExcelInfoId } });
+}
+</script>
+
+<template>
+    <div class="page">
+        <div class="filter-box">
+            <div class="filter-item">
+                <div class="filter-label">平衡表</div>
+                <div class="filter-select" @click.stop="showPicker=true">{{ fieldValue }} <van-icon name="arrow-down"/></div>
+            </div>
+            <div class="filter-item">
+                <div class="filter-label version">版本</div>
+                <div class="filter-select"  @click.stop="showVersionPicker=true">{{ fieldValueVersion }} <van-icon name="arrow-down"/></div>
+            </div>
+            <div class="top-item"><van-icon name="chart-trending-o" size="22" @click.stop="goChart"v-if="queryData.Source === 5" /> <van-icon name="more-o" size="22" @click.stop="showMoreAction=true"/></div>
+        </div>
+        
+        <div class="top-box">
+            <div class="top-item">{{ $t('shared_table.author') }}:{{ queryData.SysUserRealName }}</div>
+            <div class="top-item">{{ $t('shared_table.recent_save_time') }}:{{ queryData.CreateTime?.slice(0,10) }}</div>
+        </div>
+        <div class="sheet-box" v-if="queryData.UniqueCode">
+            <iframe :src="link + '/sheetshow?code=' + queryData.UniqueCode" frameborder="0" width="100%" height="100%"></iframe>
+        </div>
+        <!-- 更多设置弹窗 -->
+        <van-popup
+            v-model:show="showMoreAction"
+            :position="width>650?'center':'bottom'"
+            round
+            closeable
+            :style="width>650?{ width: '400px'}:''"
+        >
+            <div class="global-pop-wrap_mobile sheet-more-action-wrap">
+                <div class="head-box">
+                    <div class="title van-ellipsis">{{queryData.ExcelName}}</div>
+                </div>
+                <div class="action-box">
+                    <template v-for="item in sheetActions" :key="item.types">
+                        <div class="action-item" @click="handleActionClick(item)">
+                            {{item.label}}
+                        </div>
+                    </template>
+                </div>
+            </div>
+        </van-popup>
+
+        <!-- 平衡表选择器 -->
+        <van-popup v-model:show="showPicker" round position="bottom">
+            <van-picker
+                :columns="childTableOpts"
+                @cancel="showPicker = false"
+                @confirm="onConfirm"
+            />
+        </van-popup>
+        <!-- 版本选择器 -->
+        <van-popup v-model:show="showVersionPicker" round position="bottom">
+            <van-picker
+                :columns="versionOpts"
+                @cancel="showVersionPicker = false"
+                @confirm="onConfirmVersion"
+            />
+        </van-popup>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.page{
+    width: 100%;
+    height: 100vh;
+    padding: 24px;
+    .filter-box {
+        font-size: 28px;
+        width: 100%;
+        height: 60px;
+        display: flex;
+        justify-content: space-between;
+        .filter-item {
+            width: 42%;
+            height: 60px;
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            .filter-label {
+                width: 30%;
+                height: 60px;
+                line-height: 60px;
+                color: #303133;
+            }
+            .version {
+                width: 20%;
+            }
+            .filter-select {
+                width: 70%;
+                height: 60px;
+                line-height: 60px;
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                border-radius: 4px;
+                padding: 0 12px;
+                // border: #303133 1px solid;
+                border: 1px solid rgba(200, 205, 217, 1)
+            }
+        }
+        .top-item {
+            width: 10%;
+            height: 60px;
+            display: flex;
+            justify-content: space-around;
+            align-items: center;
+        }
+    }
+    .top-box {
+        width: 100%;
+        height: 60px;
+        display: flex;
+        justify-content: space-between;
+        .top-item {
+            line-height: 60px;
+        }
+    }
+    .sheet-box{
+        width: 100%;
+        height: calc(100% - 60px);
+        // background-color: pink;
+    }
+    .sheet-more-action-wrap{
+        .head-box{
+            .title{
+                width: 100%;
+                padding:34px 100px;
+                box-sizing: border-box;
+                font-size: 36px;
+                font-weight: 600;
+                text-align: center;
+            }
+        }
+        .action-box{
+            .action-item{
+                text-align: center;
+                padding:32px 84px;
+                border-bottom: 1px solid #DCDFE6;
+                &:last-child{
+                    border-bottom: none;
+                }
+            }
+        }
+    }
+    @media screen and (min-width: 650px) {
+        .filter-box {
+            font-size: 16px;
+            height: 30px;
+            .filter-item {
+                height: 30px;
+                .filter-label {
+                    height: 30px;
+                    line-height: 30px;
+                }
+                .filter-select {
+                    height: 30px;
+                }
+            }
+            .top-item {
+                height: 30px;
+            }
+        }
+    }
+}
+</style> 

+ 14 - 5
src/views/sheetList/balanceList.vue

@@ -1,5 +1,5 @@
 <script setup name="SheetETAList">
-import apiSheetChart from '@/api/sheet'
+import apiSheet from '@/api/sheet'
 import {ref,reactive,watch,computed} from 'vue'
 import { useRouter } from "vue-router";
 import useLocaleStore from "@/store/modules/i18n";
@@ -39,7 +39,7 @@ getSheetList()
 //获取表格列表
 async function getSheetList(){
     const {pageSize,cid,page,IsShowMe} = listState
-    const res = await apiSheetChart.sheetList({
+    const res = await apiSheet.sheetList({
         PageSize: pageSize,
         CurrentIndex: page,
         Source: 5,
@@ -95,7 +95,7 @@ getCatalogList()
 //获取目录列表
 async function getCatalogList(){
     const {IsShowMe} = listState
-    const res = await apiSheetChart.excelClassifyList({
+    const res = await apiSheet.excelClassifyList({
         Source: 5,
         IsShowMe: IsShowMe
     })
@@ -125,12 +125,21 @@ function showFileOpt(item){
 //跳转至图表详情页
 const goSheetDetail = (item)=>{
     router.push({
-        path:'/shared/detail',
+        path:'/balance/detail',
         query:{
             id:item.ExcelInfoId,
         }
     })
 }
+
+function goSheetSearch(){
+    router.push({
+        path:'/shared/search',
+        query:{
+            Source: 5,
+        }
+    })
+}
 </script>
 
 <template>
@@ -142,7 +151,7 @@ const goSheetDetail = (item)=>{
           readonly 
           :placeholder="$t('shared_table.enter_table_name')"
           style="flex:1;padding-left:0"
-          @click-input="$router.push('/shared/search')"
+          @click-input="goSheetSearch"
         />
         <div class="list-icon icon" @click="showCatalog">
           <img src="@/assets/imgs/chartETA/list-icon.png" alt="">

+ 217 - 0
src/views/sheetList/components/AddChartToMyETA.vue

@@ -0,0 +1,217 @@
+<script setup>
+//加入我的图库
+import {ref,watch} from 'vue'
+import {showToast} from 'vant'
+import{apiMyClassifyList,apiMyChartAdd,apiAddClassify} from '@/api/myETA'
+
+const props = defineProps({
+    isShowDialog:{//是否展示弹窗
+        type:Boolean,
+        default:false
+    },
+    dialogPosition:{//弹窗显示的位置,根据屏幕大小决定
+        type:String,
+        default:'bottom'
+    },
+    chartInfo:{//图表信息
+        type:Object,
+        default:{}
+    }
+})
+
+//是否展示添加分类弹窗
+const showAddClassify = ref(false)
+//新添加的分类名称
+const addClassifyName = ref('')
+
+//我的图库分类列表
+const classifyList = ref([])
+//chartInfo中,已选择的分类ids
+const choosedIds = ref([])
+
+const showAddPop = ref(false)
+watch(()=>props.isShowDialog,()=>{
+    if(props.isShowDialog){
+        getMyClassifyList()
+    }
+    showAddPop.value = props.isShowDialog
+})
+const emits = defineEmits(['close'])
+watch(showAddPop,()=>{
+    if(!showAddPop.value){
+        emits('close')
+    }
+})
+//获取我的图库列表
+async function getMyClassifyList(){
+    const res = await apiMyClassifyList()
+    if(res.Ret!==200) return 
+    classifyList.value = res.Data.List||[]
+    choosedIds.value = props.chartInfo.MyChartClassifyId?props.chartInfo.MyChartClassifyId.split(',').map(e => Number(e)):[]
+
+}
+function closeDialog(){
+    emits('close')
+}
+//选择分类
+function handleChooseClassify(item){
+    if(choosedIds.value.includes(item.MyChartClassifyId)){
+        const index=choosedIds.value.indexOf(item.MyChartClassifyId)
+        choosedIds.value.splice(index,1)
+    }else{
+        choosedIds.value.push(item.MyChartClassifyId)
+    }
+    
+}
+//确认加入的分类
+function handleConfirmAddChart(){
+    if(choosedIds.value.length===0){
+        showToast('请选择分类')
+        return
+    }
+    apiMyChartAdd({
+        ChartInfoId:props.chartInfo.ChartInfoId,
+        MyChartClassifyId:choosedIds.value
+    }).then(res=>{
+        if(res.Ret!==200) return 
+        showToast({message:'加入我的图库成功',type:'success'})
+        closeDialog()
+
+    })
+}
+//添加新的分类
+function handleConfirmEditClassify(){
+    if(!addClassifyName.value){
+        showToast('请填写分类名称!')
+        return
+    }
+    apiAddClassify({MyChartClassifyName:addClassifyName.value}).then(res=>{
+        if(res.Ret!==200) return
+        showAddClassify.value=false
+        getMyClassifyList()
+    })
+}
+</script>
+
+<template>
+    <van-popup 
+        v-model:show="showAddPop" 
+        :position="dialogPosition"
+        round
+    >
+        <div class="select-classify-wrap">
+            <div class="top-box">
+                <span class="close" @click="closeDialog">关闭</span>
+                <span class="title">加入我的图库</span>
+                <span class="add-btn" @click="showAddClassify=true,addClassifyName=''">添加分类</span>
+            </div>
+            <ul class="classify-list">
+                <li 
+                    :class="['item',choosedIds.includes(item.MyChartClassifyId)?'active':'']" 
+                    v-for="item in classifyList" 
+                    :key="item.MyChartClassifyId"
+                    @click="handleChooseClassify(item)"
+                >{{item.MyChartClassifyName}}</li>
+            </ul>
+            <div class="block"></div>
+            <div class="confirm-btn" @click="handleConfirmAddChart">
+                <span>确定</span>
+            </div>
+        </div>
+    </van-popup>
+    <van-dialog 
+        v-model:show="showAddClassify" 
+        title="添加分类" 
+        show-cancel-button
+        confirmButtonText="确定"
+        @confirm="handleConfirmEditClassify"
+    >
+        <div class="rename-wrap">
+            <input type="text" placeholder="请输入分类名称!" v-model="addClassifyName">
+        </div>
+    </van-dialog>
+</template>
+
+<style scoped lang="scss">
+.select-classify-wrap{
+    .top-box{
+        padding:32px;
+        display: flex;
+        justify-content: space-between;
+        border-bottom: 1px solid #DCDFE6;
+        .close{
+            color:#666666;
+        }
+        .title{
+            font-size: 36px;
+        }
+        .add-btn{
+            color:$theme-color;
+        }
+    }
+    .classify-list{
+        max-height: 600px;
+        overflow-y: auto;
+        .item{
+            padding:32px;
+            border-bottom: 1px solid #DCDFE6;
+            
+            &.active{
+                color:$theme-color;
+                position: relative;
+                &::after{
+                    content: '';
+                    display: block;
+                    width: 36px;
+                    height: 36px;
+                    background-image: url('@/assets/imgs/icon_select2.png');
+                    background-size: cover;
+                    background-repeat: no-repeat;
+                    position: absolute;
+                    top: 50%;
+                    transform: translateY(-50%);
+                    right: $page-padding;
+                }
+            }
+        }
+    }
+    .confirm-btn{
+        padding:32px;
+        text-align: center;
+        color: $theme-color;
+    }
+    .block{
+        background-color: #F6F6F6;
+        height:16px;
+    }
+    
+    @media screen and (min-width:$media-width){
+        width: 375px;
+        .top-box{
+            padding:16px;
+            .title{
+                font-size: 18px;
+            }
+        }
+        .classify-list{
+            max-height: 500px;
+            overflow-y: auto;
+            .item{
+                padding:16px;
+                &.active{
+                    &::after{
+                        width:18px;
+                        height:18px;
+                    }
+                }
+            }
+        }
+        .confirm-btn{
+            padding:16px;
+        }
+        .block{
+            height:8px;
+        }
+    }
+}
+</style>

+ 13 - 4
src/views/sheetList/mixedList.vue

@@ -1,5 +1,5 @@
 <script setup name="SheetETAList">
-import apiSheetChart from '@/api/sheet'
+import apiSheet from '@/api/sheet'
 import {ref,reactive,watch,computed} from 'vue'
 import { useRouter } from "vue-router";
 import useLocaleStore from "@/store/modules/i18n";
@@ -39,7 +39,7 @@ getSheetList()
 //获取表格列表
 async function getSheetList(){
     const {pageSize,cid,page,IsShowMe} = listState
-    const res = await apiSheetChart.sheetList({
+    const res = await apiSheet.sheetList({
         PageSize: pageSize,
         CurrentIndex: page,
         Source: 3,
@@ -95,7 +95,7 @@ getCatalogList()
 //获取目录列表
 async function getCatalogList(){
     const {IsShowMe} = listState
-    const res = await apiSheetChart.excelClassifyList({
+    const res = await apiSheet.excelClassifyList({
         Source: 3,
         IsShowMe: IsShowMe
     })
@@ -131,6 +131,15 @@ const goSheetDetail = (item)=>{
         }
     })
 }
+
+function goSheetSearch(){
+    router.push({
+        path:'/shared/search',
+        query:{
+            Source: 3,
+        }
+    })
+}
 </script>
 
 <template>
@@ -142,7 +151,7 @@ const goSheetDetail = (item)=>{
           readonly 
           :placeholder="$t('shared_table.enter_table_name')"
           style="flex:1;padding-left:0"
-          @click-input="$router.push('/shared/search')"
+          @click-input="goSheetSearch"
         />
         <div class="list-icon icon" @click="showCatalog">
           <img src="@/assets/imgs/chartETA/list-icon.png" alt="">

+ 231 - 15
src/views/sheetList/sharedDetail.vue

@@ -1,32 +1,226 @@
 <script setup name="sharedDetail">
-import {nextTick, onMounted,ref,reactive} from 'vue'
-import { useRoute } from "vue-router";
-import apiSheetChart from '@/api/sheet'
+import {nextTick, onMounted, ref, computed, getCurrentInstance} from 'vue'
+import { useRoute, useRouter } from "vue-router";
+import apiSheet from '@/api/sheet'
+import { useWindowSize } from '@vueuse/core'
+import { showToast, showDialog  } from "vant";
+import {usePublicSettingStore} from '@/store/modules/publicSetting'
+const publicSettingStore = usePublicSettingStore()
+// import { useDownLoadFile } from '@/hooks/useDownLoadFile'
+const { appContext : { config: { globalProperties } } } = getCurrentInstance();
+const { width } = useWindowSize()
 const route = useRoute()
+const router = useRouter()
 const queryData = ref({})
+const link = ref(publicSettingStore.publicSetting.ChartViewUrl);
+//显示更多操作栏
+let showMoreAction = ref(false)
+
+const sheetActions = computed(() => {
+    return [
+        { label: globalProperties.$t('shared_table.refresh'), types:'flushed'},
+        { label: globalProperties.$t('shared_table.download'), types:'download'},
+        { label: globalProperties.$t('shared_table.delete'), types:'delete'},
+        { label: globalProperties.$t('shared_table.cancel'), types:'cancel'},
+    ]
+})
+
+const downExcelFileUrl = computed(() => {
+    let url = `${
+    import.meta.env.VITE_APP_API_URL
+    }/datamanage/excel_info/table/download?${localStorage.getItem("token")}`;
+    return url;
+})
 
 onMounted(() => {
-    getExcelDetail()
+    getExcelDetail( 'load' )
 })
 //获取表格列表
-async function getExcelDetail(){
-    const res = await apiSheetChart.excelDetail({
+async function getExcelDetail( source = 'refresh', ExcelInfoId = '' ){
+    const detailReq = {
         ExcelInfoId: route.query.id,
-    })
+    }
+    if(source==='delete') detailReq.ExcelInfoId = ExcelInfoId
+    const res = await apiSheet.excelDetail(detailReq)
     if(res.Ret!==200) return 
+    if(source==='refresh') showToast({ type: 'success', message: globalProperties.$t('shared_table.refresh_success') })
     queryData.value = res.Data
-    console.log(queryData.value)
+}
+
+//更多操作
+function handleActionClick(item){
+    switch(item.types){
+        case 'flushed':
+            getExcelDetail( 'refresh' )
+            showMoreAction.value = false
+            break;
+        case 'download':
+            downloadExcel(queryData.value)
+            showMoreAction.value = false
+            break;
+        case 'delete':
+            showDialog({
+                title: globalProperties.$t('shared_table.note'),
+                message: globalProperties.$t('shared_table.once_deleted'),
+                showCancelButton: true
+            }).then(() => {
+                apiSheet.excelDelete({
+                    ExcelClassifyId: queryData.value.ExcelClassifyId,
+                    ExcelInfoId: queryData.value.ExcelInfoId,
+                    Source: queryData.Source,
+                }).then(res=>{
+                    if(res.Ret!==200) return 
+                    showToast({message: globalProperties.$t('shared_table.deletion_success'),type:'success'})
+                    showMoreAction.value = false
+                    getExcelDetail('delete', res.Data.ExcelInfoId)
+                })
+            }).catch(()=>{})
+            break;
+        case 'cancel':
+            showMoreAction.value = false
+            break;
+    }
+}
+
+/* 下载数据 */
+function downloadExcel(cell) {
+    const { Source, FileUrl, ExcelInfoId, ExcelName } = cell;
+    console.log(cell);
+    
+    let value = "";
+    if (Source === 1) {
+        handleDownloadResource(FileUrl, ExcelName)
+    } else if ([2, 3,5].includes(Source)) {
+        value = `${downExcelFileUrl.value}&ExcelInfoId=${ExcelInfoId}`;
+        console.log(value);
+        
+        const a = document.createElement("a");
+        a.href = value;
+        a.target = "_blank";
+        a.download = ExcelName;
+        a.style.display = "none";
+        document.body.append(a);
+        a.click();
+    }
+}
+// 下载文件
+function handleDownloadResource(url,fileName,successCb,faileCb){
+    const b = new Base64() 
+    const arr = url.split('/')
+    const _fileName = arr[arr.length-1]
+    const fileNameTypeArr = fileName.split('.')
+    const _fileNameTypeArr = _fileName.split('.')
+    const fileNameType = fileNameTypeArr.length > 1 ? fileNameTypeArr[fileNameTypeArr.length-1] : ''
+    const _fileNameType = _fileNameTypeArr.length > 1 ? _fileNameTypeArr[_fileNameTypeArr.length-1] : ''
+    apiSheet.apiDownloadResource({
+        FileName:/* fileName||_fileName */'',
+        FileUrl:b.encode(url) 
+    }).then(res=>{
+        //bus.$parseData(response);
+        const content = res
+        const blob = new Blob([content])
+        console.log(blob);
+        
+        if ('download' in document.createElement('a')) {
+            const elink = document.createElement('a')
+            elink.download = fileNameType?fileName:(fileName+'.'+_fileNameType)
+            elink.style.display = 'none'
+            elink.href = window.URL.createObjectURL(blob)
+            document.body.appendChild(elink)
+            elink.click()
+            window.URL.revokeObjectURL(elink.href)
+            document.body.removeChild(elink)
+        } else {
+            navigator.msSaveBlob(blob, fileNameType?fileName:(fileName+'.'+_fileNameType))
+        }
+        successCb&&successCb()
+    }).catch(()=>{
+        showToast({message: '下载失败',type:'warning'})
+        faileCb&&faileCb()
+    })
+}
+
+function Base64() {
+    // private property
+    var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+    // public method for encoding
+    this.encode = function (input) {
+        var output = "";
+        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+        var i = 0;
+        input = this._utf8_encode(input);
+        while (i < input.length) {
+            chr1 = input.charCodeAt(i++);
+            chr2 = input.charCodeAt(i++);
+            chr3 = input.charCodeAt(i++);
+            enc1 = chr1 >> 2;
+            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+            enc4 = chr3 & 63;
+            if (isNaN(chr2)) {
+                enc3 = enc4 = 64;
+            } else if (isNaN(chr3)) {
+                enc4 = 64;
+            }
+            output = output +
+                _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
+                _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
+        }
+        return output;
+    }
+    // private method for UTF-8 encoding
+    this._utf8_encode = function (string) {
+        string = string.replace(/\r\n/g, "\n");
+        var utftext = "";
+        for (var n = 0; n < string.length; n++) {
+            var c = string.charCodeAt(n);
+            if (c < 128) {
+                utftext += String.fromCharCode(c);
+            } else if ((c > 127) && (c < 2048)) {
+                utftext += String.fromCharCode((c >> 6) | 192);
+                utftext += String.fromCharCode((c & 63) | 128);
+            } else {
+                utftext += String.fromCharCode((c >> 12) | 224);
+                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+                utftext += String.fromCharCode((c & 63) | 128);
+            }
+        }
+        return utftext;
+    }
 }
 </script>
 
 <template>
     <div class="page">
         <div class="top-box">
-            <div class="top-item">作者:{{ queryData.SysUserRealName }}</div>
-            <div class="top-item">最近保存时间:{{ queryData.CreateTime?.slice(0,10) }}</div>
-            <div class="top-item"><van-icon name="cross" @click.stop="IsShowCatalog=false"/></div>
+            <div class="top-item">{{ $t('shared_table.author') }}:{{ queryData.SysUserRealName }}</div>
+            <div class="top-item">{{ $t('shared_table.recent_save_time') }}:{{ queryData.CreateTime?.slice(0,10) }}</div>
+            <div class="top-item"><van-icon name="more-o" @click.stop="showMoreAction=true"/></div>
+        </div>
+        <div class="sheet-box" v-if="queryData.UniqueCode">
+            <iframe :src="link + '/sheetshow?code=' + queryData.UniqueCode" frameborder="0" width="100%" height="100%"></iframe>
         </div>
-        <div class="sheet-box"></div>
+        <!-- 更多设置弹窗 -->
+        <van-popup
+            v-model:show="showMoreAction"
+            :position="width>650?'center':'bottom'"
+            round
+            closeable
+            :style="width>650?{ width: '400px'}:''"
+        >
+            <div class="global-pop-wrap_mobile sheet-more-action-wrap">
+                <div class="head-box">
+                    <div class="title van-ellipsis">{{queryData.ExcelName}}</div>
+                </div>
+                <div class="action-box">
+                    <template v-for="item in sheetActions" :key="item.types">
+                        <div class="action-item" @click="handleActionClick(item)">
+                            {{item.label}}
+                        </div>
+                    </template>
+                </div>
+            </div>
+        </van-popup>
     </div>
 </template>
 
@@ -37,7 +231,7 @@ async function getExcelDetail(){
     padding: 24px;
     .top-box {
         width: 100%;
-        height: 40px;
+        height: 60px;
         display: flex;
         justify-content: space-between;
         .item {
@@ -46,8 +240,30 @@ async function getExcelDetail(){
     }
     .sheet-box{
         width: 100%;
-        height: 100%;
-        background-color: pink;
+        height: calc(100% - 60px);
+        // background-color: pink;
+    }
+    .sheet-more-action-wrap{
+        .head-box{
+            .title{
+                width: 100%;
+                padding:34px 100px;
+                box-sizing: border-box;
+                font-size: 36px;
+                font-weight: 600;
+                text-align: center;
+            }
+        }
+        .action-box{
+            .action-item{
+                text-align: center;
+                padding:32px 84px;
+                border-bottom: 1px solid #DCDFE6;
+                &:last-child{
+                    border-bottom: none;
+                }
+            }
+        }
     }
 }
 </style> 

+ 13 - 4
src/views/sheetList/sharedList.vue

@@ -1,5 +1,5 @@
 <script setup name="SheetETAList">
-import apiSheetChart from '@/api/sheet'
+import apiSheet from '@/api/sheet'
 import {ref,reactive,watch,computed} from 'vue'
 import { useRouter } from "vue-router";
 import useLocaleStore from "@/store/modules/i18n";
@@ -39,7 +39,7 @@ getSheetList()
 //获取表格列表
 async function getSheetList(){
     const {pageSize,cid,page,IsShowMe} = listState
-    const res = await apiSheetChart.sheetList({
+    const res = await apiSheet.sheetList({
         PageSize: pageSize,
         CurrentIndex: page,
         Source: 1,
@@ -95,7 +95,7 @@ getCatalogList()
 //获取目录列表
 async function getCatalogList(){
     const {IsShowMe} = listState
-    const res = await apiSheetChart.excelClassifyList({
+    const res = await apiSheet.excelClassifyList({
         Source: 1,
         IsShowMe: IsShowMe
     })
@@ -131,6 +131,15 @@ const goSheetDetail = (item)=>{
         }
     })
 }
+
+function goSheetSearch(){
+    router.push({
+        path:'/shared/search',
+        query:{
+            Source: 1,
+        }
+    })
+}
 </script>
 
 <template>
@@ -142,7 +151,7 @@ const goSheetDetail = (item)=>{
           readonly 
           :placeholder="$t('shared_table.enter_table_name')"
           style="flex:1;padding-left:0"
-          @click-input="$router.push('/shared/search')"
+          @click-input="goSheetSearch"
         />
         <div class="list-icon icon" @click="showCatalog">
           <img src="@/assets/imgs/chartETA/list-icon.png" alt="">

+ 13 - 10
src/views/sheetList/sharedSearch.vue

@@ -1,12 +1,12 @@
 <script setup name="ChartETASearch">
 import {ref,reactive} from 'vue'
-import apiSheetChart from '@/api/sheet'
+import apiSheet from '@/api/sheet'
 import { showToast } from 'vant'
 import moment from 'moment'
-import { useRouter } from 'vue-router'
+import { useRouter, useRoute } from 'vue-router'
 import { useNoAuth } from '@/hooks/useNoAuth'
-
-const router=useRouter()
+const router = useRouter()
+const route = useRoute()
 
 const keyword=ref('')
 const listState = reactive({
@@ -17,11 +17,12 @@ const listState = reactive({
     loading:false
 })
 async function getList(){
-    const res=await apiSheetChart.sheetList({
-        CurrentIndex:listState.page,
-        PageSize:listState.pageSize,
-        Keyword:keyword.value,
-        IsShowMe:false
+    const res=await apiSheet.sheetList({
+        CurrentIndex: listState.page,
+        PageSize: listState.pageSize,
+        Keyword: keyword.value,
+        IsShowMe: false,
+        Source: route.query.Source
     })
     if(res.Ret===200){
         listState.loading=false
@@ -52,8 +53,10 @@ function handleSearch(){
 }
 
 function goDetail(item){
+    let path = '/shared/detail'
+    if(item.Source === 5) path = '/balance/detail'
     router.push({
-        path:'/shared/detail',
+        path,
         query:{
             id:item.ExcelInfoId,
         }

+ 13 - 4
src/views/sheetList/timelineList.vue

@@ -1,5 +1,5 @@
 <script setup name="SheetETAList">
-import apiSheetChart from '@/api/sheet'
+import apiSheet from '@/api/sheet'
 import {ref,reactive,watch,computed} from 'vue'
 import { useRouter } from "vue-router";
 import useLocaleStore from "@/store/modules/i18n";
@@ -39,7 +39,7 @@ getSheetList()
 //获取表格列表
 async function getSheetList(){
     const {pageSize,cid,page,IsShowMe} = listState
-    const res = await apiSheetChart.sheetList({
+    const res = await apiSheet.sheetList({
         PageSize: pageSize,
         CurrentIndex: page,
         Source: 2,
@@ -95,7 +95,7 @@ getCatalogList()
 //获取目录列表
 async function getCatalogList(){
     const {IsShowMe} = listState
-    const res = await apiSheetChart.excelClassifyList({
+    const res = await apiSheet.excelClassifyList({
         Source: 2,
         IsShowMe: IsShowMe
     })
@@ -131,6 +131,15 @@ const goSheetDetail = (item)=>{
         }
     })
 }
+
+function goSheetSearch(){
+    router.push({
+        path:'/shared/search',
+        query:{
+            Source: 2,
+        }
+    })
+}
 </script>
 
 <template>
@@ -142,7 +151,7 @@ const goSheetDetail = (item)=>{
           readonly 
           :placeholder="$t('shared_table.enter_table_name')"
           style="flex:1;padding-left:0"
-          @click-input="$router.push('/shared/search')"
+          @click-input="goSheetSearch"
         />
         <div class="list-icon icon" @click="showCatalog">
           <img src="@/assets/imgs/chartETA/list-icon.png" alt="">

+ 4 - 4
src/views/tabbar/User.vue

@@ -36,15 +36,15 @@ getSetTitle()
         </div>
         <div class="info-list">
             <div class="item">
-                <span>公司名称:</span>
+                <span>{{ $t('my.company_name') }}:</span>
                 <span>{{ CompanyName }}</span>
             </div>
             <div class="item">
-                <span>所属部门:</span>
+                <span>{{ $t('my.department') }}:</span>
                 <span>{{userInfo.GroupName || userInfo.DepartmentName}}</span>
             </div>
             <div class="item">
-                <span>角色:</span>
+                <span>{{ $t('my.role') }}:</span>
                 <span>{{userInfo.RoleName}}</span>
             </div>
         </div>
@@ -54,7 +54,7 @@ getSetTitle()
             block 
             type="primary"
             @click="handleLoginOut"
-        >退出登录</van-button>
+        >{{ $t('my.log_out') }}</van-button>
     </div>
 </template>