@fui-org/fui-cli 1.3.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/dist/fui-bmg5pnmq.js +379 -0
- package/dist/fui.js +1 -1
- package/package.json +7 -6
- package/skills/fui/SKILL.md +9 -41
- package/skills/fui-skill/SKILL.md +95 -225
- package/skills/fui-skill/assets/projectdefaultstyle-3.0.css +555 -0
- package/skills/fui-skill/assets/projectdefaultstyle.css +207 -235
- package/skills/fui-skill/references/INDEX.md +105 -137
- package/skills/fui-skill/references/advanced-techniques.md +76 -68
- package/skills/fui-skill/references/coding-standards.md +56 -56
- package/skills/fui-skill/references/component-design.md +166 -173
- package/skills/fui-skill/references/component-quickref.md +61 -60
- package/skills/fui-skill/references/component-table.md +128 -117
- package/skills/fui-skill/references/components-dialog.md +55 -56
- package/skills/fui-skill/references/components-display.md +24 -30
- package/skills/fui-skill/references/components-echart.md +186 -261
- package/skills/fui-skill/references/components-input.md +72 -96
- package/skills/fui-skill/references/controls-patterns.md +196 -342
- package/skills/fui-skill/references/controls-styling-vocabulary.md +130 -97
- package/skills/fui-skill/references/db-table-design.md +24 -28
- package/skills/fui-skill/references/db-workflow.md +191 -390
- package/skills/fui-skill/references/default-function.md +169 -128
- package/skills/fui-skill/references/design-modes.md +35 -63
- package/skills/fui-skill/references/echart-templates.md +204 -196
- package/skills/fui-skill/references/fastproject.md +62 -60
- package/skills/fui-skill/references/fsheet.md +109 -124
- package/skills/fui-skill/references/fullstack-workflow.md +90 -128
- package/skills/fui-skill/references/module-data-patterns.md +31 -40
- package/skills/fui-skill/references/module-json-anatomy.md +47 -52
- package/skills/fui-skill/references/module-structure.md +80 -196
- package/skills/fui-skill/references/new-session.md +49 -51
- package/skills/fui-skill/references/pdfmake.md +17 -17
- package/skills/fui-skill/references/permission-system.md +89 -108
- package/skills/fui-skill/references/platform-architecture.md +128 -153
- package/skills/fui-skill/references/project-config.md +102 -134
- package/skills/fui-skill/references/project-provisioning.md +139 -244
- package/skills/fui-skill/references/script-map.md +208 -242
- package/skills/fui-skill/references/sql-clr-functions.md +98 -97
- package/skills/fui-skill/references/system-design.md +63 -88
- package/skills/fui-skill/references/tapi-file-api.md +46 -52
- package/skills/fui-skill/references/tapi-permission-patterns.md +51 -53
- package/skills/fui-skill/references/tapi-reference.md +132 -207
- package/skills/fui-skill/references/tools-registry.md +84 -460
- package/skills/fui-skill/references/ui-crosswindow-patterns.md +79 -75
- package/skills/fui-skill/references/ui-dialog-patterns.md +98 -72
- package/skills/fui-skill/references/ui-layout-patterns.md +26 -26
- package/skills/fui-skill/references/ui-patterns.md +71 -83
- package/skills/fui-skill/references/ui-screenshot-review.md +63 -62
- package/skills/fui-skill/references/ui-table-cell-patterns.md +61 -59
- package/skills/fui-skill/references/ui-templates.md +16 -23
- package/skills/fui-skill/references/verification.md +236 -246
- package/skills/fui-skill/references/watcher-patterns.md +30 -63
- package/skills/fui-skill/references/websocket-realtime.md +83 -66
- package/skills/fui-skill/scripts/component-3.0.js +298 -131
- package/skills/fui-skill/scripts/component.js +277 -271
- package/skills/fui-skill/scripts/componentTable-3.0.js +182 -53
- package/skills/fui-skill/scripts/componentTable.js +171 -49
- package/skills/fui-skill/scripts/defaultfunction-3.0.js +88 -3
- package/skills/fui-skill/scripts/defaultfunction.js +88 -3
- package/skills/fui-skill/scripts/fsheet.js +38 -0
- package/dist/fui-x7kph78r.js +0 -420
- package/skills/fui-skill/README.md +0 -112
- package/skills/fui-skill/metadata.json +0 -75
- package/skills/fui-skill-cli/SKILL.md +0 -139
- package/skills/fui-skill-cli/references/INDEX.md +0 -110
- package/skills/fui-skill-cli/references/advanced-techniques.md +0 -168
- package/skills/fui-skill-cli/references/coding-standards.md +0 -112
- package/skills/fui-skill-cli/references/component-design.md +0 -448
- package/skills/fui-skill-cli/references/component-quickref.md +0 -78
- package/skills/fui-skill-cli/references/component-table.md +0 -248
- package/skills/fui-skill-cli/references/components-dialog.md +0 -191
- package/skills/fui-skill-cli/references/components-display.md +0 -141
- package/skills/fui-skill-cli/references/components-echart.md +0 -316
- package/skills/fui-skill-cli/references/components-input.md +0 -335
- package/skills/fui-skill-cli/references/controls-patterns.md +0 -701
- package/skills/fui-skill-cli/references/controls-styling-vocabulary.md +0 -137
- package/skills/fui-skill-cli/references/db-table-design.md +0 -73
- package/skills/fui-skill-cli/references/db-workflow.md +0 -288
- package/skills/fui-skill-cli/references/default-function.md +0 -425
- package/skills/fui-skill-cli/references/design-modes.md +0 -57
- package/skills/fui-skill-cli/references/echart-templates.md +0 -489
- package/skills/fui-skill-cli/references/fastproject.md +0 -99
- package/skills/fui-skill-cli/references/fsheet.md +0 -203
- package/skills/fui-skill-cli/references/fullstack-workflow.md +0 -313
- package/skills/fui-skill-cli/references/module-data-patterns.md +0 -117
- package/skills/fui-skill-cli/references/module-json-anatomy.md +0 -132
- package/skills/fui-skill-cli/references/module-structure.md +0 -141
- package/skills/fui-skill-cli/references/new-session.md +0 -85
- package/skills/fui-skill-cli/references/pdfmake.md +0 -60
- package/skills/fui-skill-cli/references/permission-system.md +0 -150
- package/skills/fui-skill-cli/references/platform-architecture.md +0 -269
- package/skills/fui-skill-cli/references/project-config.md +0 -303
- package/skills/fui-skill-cli/references/project-provisioning.md +0 -278
- package/skills/fui-skill-cli/references/script-map.md +0 -262
- package/skills/fui-skill-cli/references/sql-clr-functions.md +0 -225
- package/skills/fui-skill-cli/references/system-design.md +0 -89
- package/skills/fui-skill-cli/references/tapi-file-api.md +0 -185
- package/skills/fui-skill-cli/references/tapi-permission-patterns.md +0 -156
- package/skills/fui-skill-cli/references/tapi-reference.md +0 -474
- package/skills/fui-skill-cli/references/tools-registry.md +0 -84
- package/skills/fui-skill-cli/references/ui-crosswindow-patterns.md +0 -321
- package/skills/fui-skill-cli/references/ui-dialog-patterns.md +0 -255
- package/skills/fui-skill-cli/references/ui-layout-patterns.md +0 -176
- package/skills/fui-skill-cli/references/ui-patterns.md +0 -303
- package/skills/fui-skill-cli/references/ui-screenshot-review.md +0 -95
- package/skills/fui-skill-cli/references/ui-table-cell-patterns.md +0 -318
- package/skills/fui-skill-cli/references/ui-templates.md +0 -22
- package/skills/fui-skill-cli/references/verification.md +0 -236
- package/skills/fui-skill-cli/references/watcher-patterns.md +0 -163
- package/skills/fui-skill-cli/references/websocket-realtime.md +0 -271
|
@@ -1872,151 +1872,6 @@ Vue.component('fp-profile', {
|
|
|
1872
1872
|
},
|
|
1873
1873
|
});
|
|
1874
1874
|
//===========================================================================================
|
|
1875
|
-
// Vue.component('header-bar', {
|
|
1876
|
-
// props: {
|
|
1877
|
-
// menu: {
|
|
1878
|
-
// default: null
|
|
1879
|
-
// },
|
|
1880
|
-
// menuLeft: {
|
|
1881
|
-
// default: null
|
|
1882
|
-
// },
|
|
1883
|
-
// title: {
|
|
1884
|
-
// default: ''
|
|
1885
|
-
// },
|
|
1886
|
-
// logo: {
|
|
1887
|
-
// default: ""
|
|
1888
|
-
// },
|
|
1889
|
-
// expand: {
|
|
1890
|
-
// default: true
|
|
1891
|
-
// },
|
|
1892
|
-
// menuStyle: {
|
|
1893
|
-
// default: {
|
|
1894
|
-
// topmenu: {
|
|
1895
|
-
// bgcolor: "primary",
|
|
1896
|
-
// textcolor: "white"
|
|
1897
|
-
// },
|
|
1898
|
-
// leftmenu: {
|
|
1899
|
-
// bgcolor: "primary",
|
|
1900
|
-
// textcolor: "white"
|
|
1901
|
-
// }
|
|
1902
|
-
// }
|
|
1903
|
-
// },
|
|
1904
|
-
// component: {
|
|
1905
|
-
// default: null
|
|
1906
|
-
// },
|
|
1907
|
-
// userProfile: {
|
|
1908
|
-
// default: null
|
|
1909
|
-
// },
|
|
1910
|
-
// },
|
|
1911
|
-
// template: `<div>
|
|
1912
|
-
// <v-app-bar v-if="isMobile || menu" light app dense flat clipped-left :color="menuStyle.topmenu.bgcolor" :style="{ 'box-shadow': 'rgba(0, 0, 0, 0.5) 0px 0px '+ boxShadow +'px 0px !important'}">
|
|
1913
|
-
// <v-img class="mr-3" v-if="logo" :src="logo" height="26" width="26" max-height="26" max-width="26" contain></v-img>
|
|
1914
|
-
// <v-toolbar-title v-if="title" :class="[menuStyle.topmenu.textcolor + '--text']">{{title}}</v-toolbar-title>
|
|
1915
|
-
// <v-spacer></v-spacer>
|
|
1916
|
-
// <v-toolbar-items class="hidden-sm-and-down" v-if="menu">
|
|
1917
|
-
// <v-menu offset-y v-for="(item, index) in menu.filter(info => checkright(info))" :key="index">
|
|
1918
|
-
// <template v-slot:activator="{ on }">
|
|
1919
|
-
// <v-btn v-on="on" :href="(!item.submenu?item.url:'')" :target="item.target" text :class="[menuStyle.topmenu.textcolor + '--text']">
|
|
1920
|
-
// <v-icon v-if="item.icon" v-text="item.icon" left class="mr-1"></v-icon>
|
|
1921
|
-
// {{ item.name }}<v-icon v-if="!!item.submenu">mdi-chevron-down</v-icon>
|
|
1922
|
-
// </v-btn>
|
|
1923
|
-
// </template>
|
|
1924
|
-
// <v-list v-if="!!item.submenu">
|
|
1925
|
-
// <v-list-item v-for="(i, index) in item.submenu.filter(info => checkright(info))" :key="index" :href="i.url" :target="i.target">
|
|
1926
|
-
// <v-list-item-title>{{ i.name }}</v-list-item-title>
|
|
1927
|
-
// </v-list-item>
|
|
1928
|
-
// </v-list>
|
|
1929
|
-
// </v-menu>
|
|
1930
|
-
// </v-toolbar-items>
|
|
1931
|
-
// <component v-if="!isMobile && component" v-for="(c, index) in component" :key="index" v-bind:is="c.el" v-bind="c.attr"></component>
|
|
1932
|
-
// <fp-profile v-if="!isMobile && userProfile" v-bind="userProfile"></fp-profile>
|
|
1933
|
-
// <v-app-bar-nav-icon v-if="isMobile && leftMenuAndMobile" :color="menuStyle.topmenu.textcolor" @click.stop="openLeftMenu=!openLeftMenu"></v-app-bar-nav-icon>
|
|
1934
|
-
// </v-app-bar>
|
|
1935
|
-
|
|
1936
|
-
// <v-navigation-drawer v-if="leftMenuAndMobile" :class="[menuStyle.leftmenu.bgcolor, menuStyle.leftmenu.textcolor + '--text','fpScrollbar']" v-model="openLeftMenu" app clipped :right="isMobile" :permanent="!isMobile" :mini-variant="pinLeftMenu && !isMobile" :expand-on-hover="pinLeftMenu && !isMobile">
|
|
1937
|
-
// <template v-slot:prepend v-if="!isMobile && !menu">
|
|
1938
|
-
// <v-list-item class="px-0 pt-1 pl-3">
|
|
1939
|
-
// <v-img class="mr-1" v-if="logo" :src="logo" height="30" width="30" max-height="30" max-width="30" contain></v-img>
|
|
1940
|
-
// <v-toolbar-title v-if="title" :class="[menuStyle.leftmenu.textcolor + '--text']">{{title}}</v-toolbar-title>
|
|
1941
|
-
// </v-list-item>
|
|
1942
|
-
// </template>
|
|
1943
|
-
// <v-list class="text-left pa-0" :color="menuStyle.leftmenu.bgcolor" dark v-if="leftMenuAndMobile">
|
|
1944
|
-
// <template v-for="(item, index) in leftMenuAndMobile.filter(info => checkright(info))" :key="index">
|
|
1945
|
-
// <v-list-item v-if="!item.submenu" :href="item.url" :target="item.target">
|
|
1946
|
-
// <v-icon v-if="item.icon" v-text="item.icon" class="mr-2"></v-icon>
|
|
1947
|
-
// <v-list-item-title :class="[menuStyle.leftmenu.textcolor + '--text']">{{ item.name }}</v-list-item-title>
|
|
1948
|
-
// </v-list-item>
|
|
1949
|
-
// <v-list-group v-if="item.submenu" v-model="item.active" :color="menuStyle.leftmenu.textcolor">
|
|
1950
|
-
// <template v-slot:activator>
|
|
1951
|
-
// <v-icon v-if="item.icon" v-text="item.icon" class="mr-2"></v-icon>
|
|
1952
|
-
// <v-list-item-title :class="[menuStyle.leftmenu.textcolor + '--text']">{{ item.name }}</v-list-item-title>
|
|
1953
|
-
// </template>
|
|
1954
|
-
// <v-list-item v-for="(i, index) in item.submenu.filter(info => checkright(info))" :key="index" :href="i.url" :target="i.target">
|
|
1955
|
-
// <v-list-item-subtitle class="ml-6" :class="[menuStyle.leftmenu.textcolor + '--text']">{{ i.name }}</v-list-item-title>
|
|
1956
|
-
// </v-list-item>
|
|
1957
|
-
// </v-list-group>
|
|
1958
|
-
// </template>
|
|
1959
|
-
// </v-list>
|
|
1960
|
-
// <template v-slot:append>
|
|
1961
|
-
// <component v-if="!menu && component || isMobile" v-for="(c, index) in component" :key="index" v-bind:is="c.el" v-bind="c.attr"></component>
|
|
1962
|
-
// <v-list-item class="pa-0">
|
|
1963
|
-
// <fp-profile v-if="!menu && userProfile || isMobile" v-bind="userProfile" open-on-hover></fp-profile>
|
|
1964
|
-
// <v-spacer></v-spacer>
|
|
1965
|
-
// <v-btn @click.stop="on_of_menu" v-if="(menu || leftMenuAndMobile) && !isMobile" :color="menuStyle.topmenu.textcolor" icon><v-icon :color="menuStyle.leftmenu.textcolor">{{ pinLeftMenu?'mdi-pin-outline':'mdi-pin-off-outline'}}</v-icon></v-btn>
|
|
1966
|
-
// </v-list-item>
|
|
1967
|
-
// </template>
|
|
1968
|
-
// </v-navigation-drawer>
|
|
1969
|
-
// </div>`,
|
|
1970
|
-
// data: function () {
|
|
1971
|
-
// var leftMenuAndMobile = []
|
|
1972
|
-
// if (this.$vuetify.breakpoint.smAndDown) {
|
|
1973
|
-
// _.forEach(_.union(this.menu, this.menuLeft), function (value) {
|
|
1974
|
-
// if (rightTest(value.right)) leftMenuAndMobile.push(value);
|
|
1975
|
-
// });
|
|
1976
|
-
|
|
1977
|
-
// if (leftMenuAndMobile.length == 0) leftMenuAndMobile = null;
|
|
1978
|
-
// }
|
|
1979
|
-
// else leftMenuAndMobile = this.menuLeft;
|
|
1980
|
-
|
|
1981
|
-
// return {
|
|
1982
|
-
// leftMenuAndMobile: leftMenuAndMobile,
|
|
1983
|
-
// openLeftMenu: this.isMobile,
|
|
1984
|
-
// pinLeftMenu: (sessionStorage.getItem("fpOpenmenuAppBar") == null ? !this.expand : sessionStorage.getItem("fpOpenmenuAppBar") == 'true'),
|
|
1985
|
-
// boxShadow: 0
|
|
1986
|
-
// }
|
|
1987
|
-
// },
|
|
1988
|
-
// computed: {
|
|
1989
|
-
// isMobile() {
|
|
1990
|
-
// if (this.$vuetify.breakpoint.smAndDown) return true
|
|
1991
|
-
// else return false;
|
|
1992
|
-
// }
|
|
1993
|
-
// },
|
|
1994
|
-
// methods: {
|
|
1995
|
-
// on_of_menu: function (e) {
|
|
1996
|
-
// this.pinLeftMenu = !this.pinLeftMenu
|
|
1997
|
-
// sessionStorage.setItem("fpOpenmenuAppBar", this.pinLeftMenu);
|
|
1998
|
-
// },
|
|
1999
|
-
// checkright: function (item) {
|
|
2000
|
-
// if (!item.right) return true;
|
|
2001
|
-
// if (!vueData.user) return false;
|
|
2002
|
-
|
|
2003
|
-
// return rightTest(item.right);
|
|
2004
|
-
// }
|
|
2005
|
-
// },
|
|
2006
|
-
// created() {
|
|
2007
|
-
// if (!this.$isServer) {
|
|
2008
|
-
// this._scrollListener = () => {
|
|
2009
|
-
// this.boxShadow = Math.round(window.pageYOffset) / 10;
|
|
2010
|
-
// if (this.boxShadow > 10) this.boxShadow = 10;
|
|
2011
|
-
// }
|
|
2012
|
-
// this._scrollListener()
|
|
2013
|
-
// window.addEventListener('scroll', this._scrollListener)
|
|
2014
|
-
// }
|
|
2015
|
-
// },
|
|
2016
|
-
// beforeDestroy() {
|
|
2017
|
-
// window.removeEventListener('scroll', this._scrollListener)
|
|
2018
|
-
// }
|
|
2019
|
-
// });
|
|
2020
1875
|
Vue.component('header-bar', {
|
|
2021
1876
|
props: {
|
|
2022
1877
|
menu: {
|
|
@@ -2083,7 +1938,7 @@ Vue.component('header-bar', {
|
|
|
2083
1938
|
<v-navigation-drawer \
|
|
2084
1939
|
v-if=\"leftMenuAndMobile\" \
|
|
2085
1940
|
ref=\"navDrawer\"\
|
|
2086
|
-
:class=\"[menuStyle.leftmenu.bgcolor, menuStyle.leftmenu.textcolor + '--text'
|
|
1941
|
+
:class=\"[menuStyle.leftmenu.bgcolor, menuStyle.leftmenu.textcolor + '--text']\" \
|
|
2087
1942
|
v-model=\"openLeftMenu\" \
|
|
2088
1943
|
app \
|
|
2089
1944
|
clipped \
|
|
@@ -2216,39 +2071,6 @@ Vue.component('header-bar', {
|
|
|
2216
2071
|
}
|
|
2217
2072
|
}, 300);
|
|
2218
2073
|
});
|
|
2219
|
-
},
|
|
2220
|
-
injectScrollbarStyles: function () {
|
|
2221
|
-
if (!this.leftMenuAndMobile || this.leftMenuAndMobile.length === 0) return;
|
|
2222
|
-
|
|
2223
|
-
var styleId = 'fp-custom-scrollbar-style';
|
|
2224
|
-
if (document.getElementById(styleId)) return;
|
|
2225
|
-
|
|
2226
|
-
var thumbColor = 'rgba(255,255,255,0.3)';
|
|
2227
|
-
var thumbHoverColor = 'rgba(255,255,255,0.5)';
|
|
2228
|
-
var trackColor = 'rgba(0,0,0,0.1)';
|
|
2229
|
-
|
|
2230
|
-
var style = document.createElement('style');
|
|
2231
|
-
style.id = styleId;
|
|
2232
|
-
style.textContent = [
|
|
2233
|
-
'.fp-custom-scrollbar .v-navigation-drawer__content::-webkit-scrollbar {',
|
|
2234
|
-
' width: 5px;',
|
|
2235
|
-
'}',
|
|
2236
|
-
'.fp-custom-scrollbar .v-navigation-drawer__content::-webkit-scrollbar-track {',
|
|
2237
|
-
' background: ' + trackColor + ';',
|
|
2238
|
-
'}',
|
|
2239
|
-
'.fp-custom-scrollbar .v-navigation-drawer__content::-webkit-scrollbar-thumb {',
|
|
2240
|
-
' background: ' + thumbColor + ';',
|
|
2241
|
-
' border-radius: 3px;',
|
|
2242
|
-
'}',
|
|
2243
|
-
'.fp-custom-scrollbar .v-navigation-drawer__content::-webkit-scrollbar-thumb:hover {',
|
|
2244
|
-
' background: ' + thumbHoverColor + ';',
|
|
2245
|
-
'}',
|
|
2246
|
-
'.fp-custom-scrollbar .v-navigation-drawer__content {',
|
|
2247
|
-
' scrollbar-width: thin;',
|
|
2248
|
-
' scrollbar-color: ' + thumbColor + ' ' + trackColor + ';',
|
|
2249
|
-
'}'
|
|
2250
|
-
].join('\n');
|
|
2251
|
-
document.head.appendChild(style);
|
|
2252
2074
|
}
|
|
2253
2075
|
},
|
|
2254
2076
|
created: function () {
|
|
@@ -2264,7 +2086,8 @@ Vue.component('header-bar', {
|
|
|
2264
2086
|
},
|
|
2265
2087
|
mounted: function () {
|
|
2266
2088
|
if (this.leftMenuAndMobile && this.leftMenuAndMobile.length > 0) {
|
|
2267
|
-
|
|
2089
|
+
// Thanh cuộn menu trái lấy từ .fpScrollbar trong projectdefaultstyle.css
|
|
2090
|
+
// (rule đó đã phủ sẵn .v-navigation-drawer__content).
|
|
2268
2091
|
this.scrollToActiveMenu();
|
|
2269
2092
|
}
|
|
2270
2093
|
},
|
|
@@ -2941,30 +2764,82 @@ Vue.component('f-excel-reader', {
|
|
|
2941
2764
|
dateFormat: { type: String, default: 'dd/mm/yyyy' },
|
|
2942
2765
|
defval: { type: [String, Number], default: '' },
|
|
2943
2766
|
|
|
2944
|
-
sheet:
|
|
2945
|
-
|
|
2767
|
+
sheet: { type: [String, Number], default: null },
|
|
2768
|
+
sheetPicker: { type: [Boolean, String], default: true },
|
|
2769
|
+
sheetStats: { type: [Boolean, String], default: false },
|
|
2770
|
+
skipRows: { type: [String, Number], default: 0 },
|
|
2946
2771
|
|
|
2947
|
-
label:
|
|
2948
|
-
iconText:
|
|
2949
|
-
accept:
|
|
2950
|
-
maxSize:
|
|
2772
|
+
label: { type: String, default: 'Load Excel' },
|
|
2773
|
+
iconText: { type: String, default: 'mdi-grid' },
|
|
2774
|
+
accept: { type: String, default: '.xlsx,.xlsb,.xlsm,.xls,.csv,.txt,.ods,.dbf,.prn,.html,.htm' },
|
|
2775
|
+
maxSize: { type: [String, Number], default: 20 },
|
|
2951
2776
|
loadTimeout: { type: [String, Number], default: 15000 },
|
|
2777
|
+
loadingText: { type: String, default: 'Đang xử lý dữ liệu...' },
|
|
2778
|
+
|
|
2779
|
+
notify: { type: [Boolean, String], default: true },
|
|
2780
|
+
notifyTimeout: { type: [String, Number], default: 6000 },
|
|
2952
2781
|
|
|
2953
2782
|
action: { type: Object, default: null }
|
|
2954
2783
|
},
|
|
2955
2784
|
template: `
|
|
2956
2785
|
<div>
|
|
2957
2786
|
<v-btn v-bind="$attrs" v-on="$listeners"
|
|
2958
|
-
:disabled="parsing"
|
|
2787
|
+
:disabled="parsing" @click="onButtonClick">
|
|
2959
2788
|
<v-icon v-if="iconText" left>{{ iconText }}</v-icon>
|
|
2960
2789
|
{{ label }}
|
|
2961
2790
|
</v-btn>
|
|
2962
|
-
<input ref="uploader" style="display:none" type="file" :multiple="false"
|
|
2963
|
-
|
|
2791
|
+
<input ref="uploader" style="display:none" type="file" :multiple="false" :accept="accept" @change="onchange" onclick="this.value=null;">
|
|
2792
|
+
|
|
2793
|
+
<v-dialog v-model="loading" persistent width="320">
|
|
2794
|
+
<v-card class="pa-8 d-flex flex-column align-center justify-center text-center">
|
|
2795
|
+
<div class="fui-loading"></div>
|
|
2796
|
+
<div class="mt-5 body-2">{{ loadingMsg }}</div>
|
|
2797
|
+
<div v-if="loadingSub" class="mt-1 caption grey--text text-truncate" style="max-width:100%;">{{ loadingSub }}</div>
|
|
2798
|
+
</v-card>
|
|
2799
|
+
</v-dialog>
|
|
2800
|
+
|
|
2801
|
+
<v-dialog v-model="dialog" max-width="440" persistent>
|
|
2802
|
+
<v-card>
|
|
2803
|
+
<v-card-title class="subtitle-1">Chọn sheet cần đọc</v-card-title>
|
|
2804
|
+
<v-card-text class="pb-0">
|
|
2805
|
+
<div class="caption mb-2">{{ pendingName }}</div>
|
|
2806
|
+
<v-list dense style="max-height:300px; overflow-y:auto;">
|
|
2807
|
+
<v-list-item v-for="s in sheetList" :key="s.name"
|
|
2808
|
+
@click="sheetChoice = s.name">
|
|
2809
|
+
<v-list-item-content>
|
|
2810
|
+
<v-list-item-title
|
|
2811
|
+
:class="{ 'primary--text font-weight-bold': s.name === sheetChoice }">
|
|
2812
|
+
{{ s.name }}
|
|
2813
|
+
</v-list-item-title>
|
|
2814
|
+
<v-list-item-subtitle v-if="s.info">{{ s.info }}</v-list-item-subtitle>
|
|
2815
|
+
</v-list-item-content>
|
|
2816
|
+
</v-list-item>
|
|
2817
|
+
</v-list>
|
|
2818
|
+
</v-card-text>
|
|
2819
|
+
<v-card-actions>
|
|
2820
|
+
<v-spacer></v-spacer>
|
|
2821
|
+
<v-btn text @click="pickSheet(null)">Hủy</v-btn>
|
|
2822
|
+
<v-btn text color="primary" :disabled="!sheetChoice"
|
|
2823
|
+
@click="pickSheet(sheetChoice)">Chọn</v-btn>
|
|
2824
|
+
</v-card-actions>
|
|
2825
|
+
</v-card>
|
|
2826
|
+
</v-dialog>
|
|
2827
|
+
|
|
2828
|
+
<v-snackbar v-model="snack" :color="snackColor" :timeout="Number(notifyTimeout)" top>
|
|
2829
|
+
{{ snackMsg }}
|
|
2830
|
+
<template #action="{ attrs }">
|
|
2831
|
+
<v-btn text v-bind="attrs" @click="snack = false">Đóng</v-btn>
|
|
2832
|
+
</template>
|
|
2833
|
+
</v-snackbar>
|
|
2964
2834
|
</div>
|
|
2965
2835
|
`,
|
|
2966
2836
|
data: function () {
|
|
2967
|
-
return {
|
|
2837
|
+
return {
|
|
2838
|
+
parsing: false, reqId: 0,
|
|
2839
|
+
loading: false, loadingMsg: '', loadingSub: '',
|
|
2840
|
+
dialog: false, sheetList: [], sheetChoice: null, pendingName: '',
|
|
2841
|
+
snack: false, snackMsg: '', snackColor: 'error'
|
|
2842
|
+
};
|
|
2968
2843
|
},
|
|
2969
2844
|
computed: {
|
|
2970
2845
|
optHeader: function () {
|
|
@@ -2976,29 +2851,56 @@ Vue.component('f-excel-reader', {
|
|
|
2976
2851
|
if (s.toUpperCase() === 'A') return 'A';
|
|
2977
2852
|
return h;
|
|
2978
2853
|
},
|
|
2979
|
-
optRaw:
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
},
|
|
2854
|
+
optRaw: function () { return this.toBool(this.raw, false); },
|
|
2855
|
+
usePicker: function () { return this.toBool(this.sheetPicker, true); },
|
|
2856
|
+
useStats: function () { return this.toBool(this.sheetStats, false); },
|
|
2857
|
+
useNotify: function () { return this.toBool(this.notify, true); },
|
|
2984
2858
|
optRange: function () {
|
|
2985
2859
|
var n = parseInt(this.skipRows, 10);
|
|
2986
2860
|
return n > 0 ? n : undefined;
|
|
2987
2861
|
},
|
|
2988
2862
|
limitMB: function () {
|
|
2989
|
-
var
|
|
2990
|
-
|
|
2863
|
+
var n = parseFloat(this.maxSize);
|
|
2864
|
+
if (isNaN(n)) return 20;
|
|
2865
|
+
return n > 0 ? n : 0;
|
|
2991
2866
|
}
|
|
2992
2867
|
},
|
|
2993
2868
|
methods: {
|
|
2994
|
-
|
|
2869
|
+
toBool: function (v, def) {
|
|
2870
|
+
if (typeof v === 'boolean') return v;
|
|
2871
|
+
if (v === null || v === undefined || v === '') return def;
|
|
2872
|
+
var s = String(v).trim().toLowerCase();
|
|
2873
|
+
if (s === 'true' || s === '1' || s === 'yes') return true;
|
|
2874
|
+
if (s === 'false' || s === '0' || s === 'no') return false;
|
|
2875
|
+
return def;
|
|
2876
|
+
},
|
|
2877
|
+
|
|
2878
|
+
nextPaint: function () {
|
|
2879
|
+
return new Promise(function (resolve) {
|
|
2880
|
+
requestAnimationFrame(function () {
|
|
2881
|
+
requestAnimationFrame(function () { setTimeout(resolve, 0); });
|
|
2882
|
+
});
|
|
2883
|
+
});
|
|
2884
|
+
},
|
|
2885
|
+
|
|
2886
|
+
showLoading: function (msg, sub) {
|
|
2995
2887
|
var ctrl = this;
|
|
2996
|
-
|
|
2997
|
-
ctrl.
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
2888
|
+
ctrl.loadingMsg = msg || ctrl.loadingText;
|
|
2889
|
+
ctrl.loadingSub = sub || '';
|
|
2890
|
+
ctrl.loading = true;
|
|
2891
|
+
return new Promise(function (resolve) {
|
|
2892
|
+
ctrl.$nextTick(function () { ctrl.nextPaint().then(resolve); });
|
|
2893
|
+
});
|
|
2894
|
+
},
|
|
2895
|
+
|
|
2896
|
+
hideLoading: function () {
|
|
2897
|
+
this.loading = false;
|
|
2898
|
+
this.loadingSub = '';
|
|
2899
|
+
},
|
|
2900
|
+
|
|
2901
|
+
onButtonClick: function () {
|
|
2902
|
+
if (this.parsing) return;
|
|
2903
|
+
this.$refs.uploader.click();
|
|
3002
2904
|
},
|
|
3003
2905
|
|
|
3004
2906
|
onchange: function (evt) {
|
|
@@ -3006,33 +2908,57 @@ Vue.component('f-excel-reader', {
|
|
|
3006
2908
|
var file = evt.target.files && evt.target.files[0];
|
|
3007
2909
|
evt.target.value = '';
|
|
3008
2910
|
|
|
3009
|
-
if (ctrl.parsing) return;
|
|
3010
|
-
if (!file)
|
|
2911
|
+
if (ctrl.parsing) return;
|
|
2912
|
+
if (!file) return;
|
|
3011
2913
|
|
|
3012
|
-
var id = ++ctrl.reqId;
|
|
3013
|
-
var parsed = false;
|
|
2914
|
+
var id = ++ctrl.reqId;
|
|
2915
|
+
var parsed = false;
|
|
2916
|
+
var u8 = null;
|
|
3014
2917
|
ctrl.parsing = true;
|
|
3015
|
-
ctrl.busy = true;
|
|
3016
2918
|
|
|
3017
2919
|
Promise.resolve()
|
|
3018
2920
|
.then(function () {
|
|
3019
|
-
if (ctrl.limitMB > 0 && file.size > ctrl.limitMB *
|
|
3020
|
-
throw new Error('File
|
|
3021
|
-
|
|
2921
|
+
if (ctrl.limitMB > 0 && file.size > ctrl.limitMB * 1048576)
|
|
2922
|
+
throw new Error('File "' + file.name + '" nặng ' + ctrl.fmtSize(file.size)
|
|
2923
|
+
+ ', vượt giới hạn ' + ctrl.limitMB + 'MB');
|
|
2924
|
+
return ctrl.showLoading('Đang mở tập tin...', file.name);
|
|
3022
2925
|
})
|
|
3023
|
-
.then(function () { return ctrl.
|
|
3024
|
-
.then(function (
|
|
3025
|
-
|
|
3026
|
-
ctrl.
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
2926
|
+
.then(function () { return ctrl.ensureXLSX(); })
|
|
2927
|
+
.then(function () { return ctrl.readBuffer(file); })
|
|
2928
|
+
.then(function (buf) {
|
|
2929
|
+
if (id !== ctrl.reqId) return null;
|
|
2930
|
+
u8 = new Uint8Array(buf);
|
|
2931
|
+
return ctrl.showLoading('Đang đọc cấu trúc tập tin...', file.name)
|
|
2932
|
+
.then(function () { return ctrl.resolveSheet(u8, file); });
|
|
2933
|
+
})
|
|
2934
|
+
.then(function (wsname) {
|
|
2935
|
+
if (id !== ctrl.reqId) return;
|
|
2936
|
+
|
|
2937
|
+
if (!wsname) {
|
|
2938
|
+
ctrl.parsing = false;
|
|
2939
|
+
ctrl.hideLoading();
|
|
2940
|
+
parsed = true;
|
|
2941
|
+
ctrl.$emit('cancel', { fileName: file.name, reason: 'sheet' });
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2945
|
+
// dialog loading cho MỌI trường hợp, kể cả file 1 sheet
|
|
2946
|
+
return ctrl.showLoading(ctrl.loadingText, 'Sheet: ' + wsname)
|
|
2947
|
+
.then(function () {
|
|
2948
|
+
if (id !== ctrl.reqId) return;
|
|
2949
|
+
|
|
2950
|
+
var res = ctrl.parseSheet(u8, wsname, file);
|
|
2951
|
+
ctrl.parsing = false;
|
|
2952
|
+
ctrl.hideLoading();
|
|
2953
|
+
parsed = true;
|
|
2954
|
+
|
|
2955
|
+
ctrl.$emit('input', res.rows);
|
|
2956
|
+
ctrl.$emit('load', res.meta);
|
|
2957
|
+
ctrl.$nextTick(function () { ctrl.runAction(file); });
|
|
2958
|
+
});
|
|
3033
2959
|
})
|
|
3034
2960
|
.catch(function (err) {
|
|
3035
|
-
if (id !== ctrl.reqId || parsed) {
|
|
2961
|
+
if (id !== ctrl.reqId || parsed) {
|
|
3036
2962
|
console.warn('[FExcelReader] bỏ qua lỗi ngoài phạm vi đọc Excel:', err);
|
|
3037
2963
|
return;
|
|
3038
2964
|
}
|
|
@@ -3040,6 +2966,107 @@ Vue.component('f-excel-reader', {
|
|
|
3040
2966
|
});
|
|
3041
2967
|
},
|
|
3042
2968
|
|
|
2969
|
+
// ---------- PHA 1 ----------
|
|
2970
|
+
listSheets: function (u8) {
|
|
2971
|
+
if (this.useStats) {
|
|
2972
|
+
var wbFull = XLSX.read(u8, { bookSheets: true, bookProps: true, dense: true });
|
|
2973
|
+
return { names: wbFull.SheetNames, wb: wbFull };
|
|
2974
|
+
}
|
|
2975
|
+
var wb = XLSX.read(u8, { bookSheets: true });
|
|
2976
|
+
return { names: wb.SheetNames, wb: null };
|
|
2977
|
+
},
|
|
2978
|
+
|
|
2979
|
+
resolveSheet: function (u8, file) {
|
|
2980
|
+
var ctrl = this;
|
|
2981
|
+
var sp = ctrl.sheet;
|
|
2982
|
+
|
|
2983
|
+
var info = ctrl.listSheets(u8);
|
|
2984
|
+
var names = info.names;
|
|
2985
|
+
if (!names || !names.length)
|
|
2986
|
+
return Promise.reject(new Error('File "' + file.name + '" không có sheet nào'));
|
|
2987
|
+
|
|
2988
|
+
if (sp !== null && sp !== undefined && sp !== '') {
|
|
2989
|
+
var s = String(sp).trim();
|
|
2990
|
+
if (/^\d+$/.test(s)) {
|
|
2991
|
+
var n = names[parseInt(s, 10)];
|
|
2992
|
+
if (!n) return Promise.reject(new Error('Không có sheet ở vị trí ' + s
|
|
2993
|
+
+ ' (file chỉ có ' + names.length + ' sheet)'));
|
|
2994
|
+
return Promise.resolve(n);
|
|
2995
|
+
}
|
|
2996
|
+
if (names.indexOf(s) === -1)
|
|
2997
|
+
return Promise.reject(new Error('Không tìm thấy sheet "' + s
|
|
2998
|
+
+ '". File có: ' + names.join(', ')));
|
|
2999
|
+
return Promise.resolve(s);
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
if (names.length === 1 || !ctrl.usePicker) return Promise.resolve(names[0]);
|
|
3003
|
+
|
|
3004
|
+
var list = [];
|
|
3005
|
+
for (var i = 0; i < names.length; i++) list.push(ctrl.sheetInfo(info.wb, names[i]));
|
|
3006
|
+
|
|
3007
|
+
var pick = list[0];
|
|
3008
|
+
for (var j = 0; j < list.length; j++) {
|
|
3009
|
+
if (!list[j].empty) { pick = list[j]; break; }
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
ctrl.sheetList = list;
|
|
3013
|
+
ctrl.sheetChoice = pick.name;
|
|
3014
|
+
ctrl.pendingName = file.name + ' — ' + names.length + ' sheet';
|
|
3015
|
+
ctrl.hideLoading();
|
|
3016
|
+
ctrl.dialog = true;
|
|
3017
|
+
|
|
3018
|
+
return new Promise(function (resolve) { ctrl._pickSheet = resolve; });
|
|
3019
|
+
},
|
|
3020
|
+
|
|
3021
|
+
sheetInfo: function (wb, name) {
|
|
3022
|
+
if (!wb) return { name: name, info: '', empty: false };
|
|
3023
|
+
var ref = wb.Sheets[name] && wb.Sheets[name]['!ref'];
|
|
3024
|
+
if (!ref) return { name: name, info: 'sheet rỗng', empty: true };
|
|
3025
|
+
var r = XLSX.utils.decode_range(ref);
|
|
3026
|
+
return {
|
|
3027
|
+
name: name,
|
|
3028
|
+
info: (r.e.r - r.s.r + 1) + ' dòng × ' + (r.e.c - r.s.c + 1) + ' cột',
|
|
3029
|
+
empty: false
|
|
3030
|
+
};
|
|
3031
|
+
},
|
|
3032
|
+
|
|
3033
|
+
pickSheet: function (name) {
|
|
3034
|
+
this.dialog = false;
|
|
3035
|
+
var done = this._pickSheet;
|
|
3036
|
+
this._pickSheet = null;
|
|
3037
|
+
if (done) done(name || null);
|
|
3038
|
+
},
|
|
3039
|
+
|
|
3040
|
+
// ---------- PHA 2 ----------
|
|
3041
|
+
parseSheet: function (u8, wsname, file) {
|
|
3042
|
+
var wb = XLSX.read(u8, { type: 'array', cellDates: true, sheets: wsname });
|
|
3043
|
+
var ws = wb.Sheets[wsname];
|
|
3044
|
+
if (!ws) throw new Error('Không đọc được sheet "' + wsname + '"');
|
|
3045
|
+
|
|
3046
|
+
var rows = XLSX.utils.sheet_to_json(ws, {
|
|
3047
|
+
header: this.optHeader,
|
|
3048
|
+
raw: this.optRaw,
|
|
3049
|
+
dateNF: this.dateFormat,
|
|
3050
|
+
defval: this.defval,
|
|
3051
|
+
range: this.optRange,
|
|
3052
|
+
blankrows: false
|
|
3053
|
+
});
|
|
3054
|
+
|
|
3055
|
+
return {
|
|
3056
|
+
rows: rows,
|
|
3057
|
+
meta: {
|
|
3058
|
+
rows: rows,
|
|
3059
|
+
rowCount: rows.length,
|
|
3060
|
+
headers: (this.optHeader === 1 && rows.length) ? rows[0] : Object.keys(rows[0] || {}),
|
|
3061
|
+
sheetName: wsname,
|
|
3062
|
+
sheetNames: wb.SheetNames,
|
|
3063
|
+
fileName: file.name,
|
|
3064
|
+
fileSize: file.size
|
|
3065
|
+
}
|
|
3066
|
+
};
|
|
3067
|
+
},
|
|
3068
|
+
|
|
3069
|
+
// ---------- phụ trợ ----------
|
|
3043
3070
|
ensureXLSX: function () {
|
|
3044
3071
|
var ctrl = this;
|
|
3045
3072
|
return new Promise(function (resolve, reject) {
|
|
@@ -3052,7 +3079,7 @@ Vue.component('f-excel-reader', {
|
|
|
3052
3079
|
var timer = setTimeout(function () {
|
|
3053
3080
|
if (settled) return;
|
|
3054
3081
|
settled = true;
|
|
3055
|
-
reject(new Error('Quá thời gian nạp thư viện
|
|
3082
|
+
reject(new Error('Quá thời gian nạp thư viện đọc Excel'));
|
|
3056
3083
|
}, ms);
|
|
3057
3084
|
|
|
3058
3085
|
new Loader().require(['/include/excel/xlsx.full.min.js'], function () {
|
|
@@ -3060,7 +3087,7 @@ Vue.component('f-excel-reader', {
|
|
|
3060
3087
|
settled = true;
|
|
3061
3088
|
clearTimeout(timer);
|
|
3062
3089
|
if (typeof XLSX !== 'undefined') resolve();
|
|
3063
|
-
else reject(new Error('Nạp thư viện
|
|
3090
|
+
else reject(new Error('Nạp thư viện đọc Excel thất bại'));
|
|
3064
3091
|
});
|
|
3065
3092
|
});
|
|
3066
3093
|
},
|
|
@@ -3069,57 +3096,20 @@ Vue.component('f-excel-reader', {
|
|
|
3069
3096
|
return new Promise(function (resolve, reject) {
|
|
3070
3097
|
var reader = new FileReader();
|
|
3071
3098
|
reader.onload = function (e) { resolve(e.target.result); };
|
|
3072
|
-
reader.onerror = function () { reject(new Error('Không đọc được file
|
|
3099
|
+
reader.onerror = function () { reject(new Error('Không đọc được file "' + file.name + '"')); };
|
|
3073
3100
|
reader.onabort = function () { reject(new Error('Việc đọc file bị hủy')); };
|
|
3074
3101
|
reader.readAsArrayBuffer(file);
|
|
3075
3102
|
});
|
|
3076
3103
|
},
|
|
3077
3104
|
|
|
3078
|
-
parseFile: function (file) {
|
|
3079
|
-
var ctrl = this;
|
|
3080
|
-
return ctrl.readBuffer(file).then(function (buf) {
|
|
3081
|
-
var wb = XLSX.read(new Uint8Array(buf), { type: 'array', cellDates: true });
|
|
3082
|
-
|
|
3083
|
-
var isIndex = typeof ctrl.sheet === 'number' || /^\d+$/.test(String(ctrl.sheet));
|
|
3084
|
-
var wsname = isIndex
|
|
3085
|
-
? wb.SheetNames[parseInt(ctrl.sheet, 10)]
|
|
3086
|
-
: (wb.SheetNames.indexOf(ctrl.sheet) !== -1 ? ctrl.sheet : wb.SheetNames[0]);
|
|
3087
|
-
|
|
3088
|
-
var ws = wb.Sheets[wsname];
|
|
3089
|
-
if (!ws) throw new Error('Không tìm thấy sheet: ' + ctrl.sheet);
|
|
3090
|
-
|
|
3091
|
-
var rows = XLSX.utils.sheet_to_json(ws, {
|
|
3092
|
-
header: ctrl.optHeader,
|
|
3093
|
-
raw: ctrl.optRaw,
|
|
3094
|
-
dateNF: ctrl.dateFormat,
|
|
3095
|
-
defval: ctrl.defval,
|
|
3096
|
-
range: ctrl.optRange,
|
|
3097
|
-
blankrows: false
|
|
3098
|
-
});
|
|
3099
|
-
|
|
3100
|
-
return {
|
|
3101
|
-
rows: rows,
|
|
3102
|
-
meta: {
|
|
3103
|
-
rows: rows,
|
|
3104
|
-
rowCount: rows.length,
|
|
3105
|
-
headers: (ctrl.optHeader === 1 && rows.length) ? rows[0] : Object.keys(rows[0] || {}),
|
|
3106
|
-
sheetName: wsname,
|
|
3107
|
-
sheetNames: wb.SheetNames,
|
|
3108
|
-
fileName: file.name
|
|
3109
|
-
}
|
|
3110
|
-
};
|
|
3111
|
-
});
|
|
3112
|
-
},
|
|
3113
|
-
|
|
3114
3105
|
runAction: function (file) {
|
|
3115
3106
|
var ctrl = this;
|
|
3116
3107
|
if (!ctrl.action || typeof CALL !== 'function') return;
|
|
3117
3108
|
var onErr = function (err) {
|
|
3109
|
+
var msg = (err && err.message) || String(err);
|
|
3118
3110
|
console.error('[FExcelReader] action lỗi (dữ liệu Excel vẫn hợp lệ):', err);
|
|
3119
|
-
ctrl
|
|
3120
|
-
|
|
3121
|
-
fileName: file.name
|
|
3122
|
-
});
|
|
3111
|
+
ctrl.toast('Đọc Excel thành công nhưng xử lý sau đó lỗi: ' + msg, 'warning');
|
|
3112
|
+
ctrl.$emit('action-error', { message: msg, fileName: file.name });
|
|
3123
3113
|
};
|
|
3124
3114
|
try {
|
|
3125
3115
|
var r = CALL(ctrl.action);
|
|
@@ -3129,14 +3119,30 @@ Vue.component('f-excel-reader', {
|
|
|
3129
3119
|
}
|
|
3130
3120
|
},
|
|
3131
3121
|
|
|
3122
|
+
toast: function (msg, color) {
|
|
3123
|
+
if (!this.useNotify) return;
|
|
3124
|
+
this.snackColor = color || 'error';
|
|
3125
|
+
this.snackMsg = msg;
|
|
3126
|
+
this.snack = true;
|
|
3127
|
+
},
|
|
3128
|
+
|
|
3129
|
+
fmtSize: function (bytes) {
|
|
3130
|
+
if (bytes >= 1048576) return (bytes / 1048576).toFixed(1) + 'MB';
|
|
3131
|
+
if (bytes >= 1024) return (bytes / 1024).toFixed(0) + 'KB';
|
|
3132
|
+
return bytes + 'B';
|
|
3133
|
+
},
|
|
3134
|
+
|
|
3132
3135
|
fail: function (err, file) {
|
|
3136
|
+
var msg = (err && err.message) || String(err);
|
|
3133
3137
|
console.error('[FExcelReader]', err);
|
|
3134
3138
|
this.parsing = false;
|
|
3135
|
-
this.
|
|
3136
|
-
this
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
});
|
|
3139
|
+
this.dialog = false;
|
|
3140
|
+
this.hideLoading();
|
|
3141
|
+
this.toast(msg, 'error');
|
|
3142
|
+
this.$emit('error', { message: msg, fileName: file ? file.name : null });
|
|
3140
3143
|
}
|
|
3144
|
+
},
|
|
3145
|
+
beforeDestroy: function () {
|
|
3146
|
+
if (this._pickSheet) { this._pickSheet(null); this._pickSheet = null; }
|
|
3141
3147
|
}
|
|
3142
3148
|
});
|