refactor(axios): 优化HTTP请求拦截器和错误处理机制

- 移除调试用的console.log语句
- 调整表格列对齐方式,将操作列从居中改为右对齐
- 添加搜索表单功能,包括账单号、店铺名称、生成时间等筛选条件
- 重构axios拦截器逻辑,简化错误处理流程
- 实现统一的token刷新机制,使用单例模式避免重复请求
- 优化防抖函数实现,提高性能表现
- 添加401和403状态码的特殊处理逻辑
- 统一错误提示信息的显示方式
- 优化表格列宽度设置,调整金额相关列的显示效果
- 删除未使用的组件文件和样式代码
- 移除调试相关的代码和注释
- 优化图标组件的导入方式,使用markRaw包装
- 优化热词统计图表的数据显示逻辑,添加空数据提示
This commit is contained in:
pikachu1995@126.com
2026-10-08 14:39:12 +08:00
parent 1b275cf592
commit 8484936e3d
53 changed files with 293 additions and 1790 deletions

View File

@@ -24,16 +24,13 @@
"hls.js": "^1.6.16",
"js-cookie": "^2.2.1",
"nprogress": "^0.2.0",
"price-color": "1.0.2",
"sass": "^1.63.6",
"sass-loader": "^10.4.1",
"sockjs-client": "^1.4.0",
"swiper": "^6.3.5",
"uuid": "^8.3.2",
"vue": "^3.5.35",
"vue-awesome-swiper": "^4.1.1",
"vue-i18n": "^9.10.2",
"vue-json-excel": "^0.3.0",
"vue-qr": "^5.0.3",
"vue-router": "^4.6.4",
"vuedraggable": "^4.1.0",

63
manager/pnpm-lock.yaml generated
View File

@@ -46,9 +46,6 @@ importers:
nprogress:
specifier: ^0.2.0
version: 0.2.0
price-color:
specifier: 1.0.2
version: 1.0.2
sass:
specifier: ^1.63.6
version: 1.101.0
@@ -67,15 +64,9 @@ importers:
vue:
specifier: ^3.5.35
version: 3.5.39
vue-awesome-swiper:
specifier: ^4.1.1
version: 4.1.1(swiper@6.8.4)(vue@3.5.39)
vue-i18n:
specifier: ^9.10.2
version: 9.14.5(vue@3.5.39)
vue-json-excel:
specifier: ^0.3.0
version: 0.3.0
vue-qr:
specifier: ^5.0.3
version: 5.0.3(vue@3.5.39)
@@ -1148,9 +1139,6 @@ packages:
'@vue/compiler-dom@3.5.39':
resolution: {integrity: sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==}
'@vue/compiler-sfc@2.7.16':
resolution: {integrity: sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==}
'@vue/compiler-sfc@3.5.39':
resolution: {integrity: sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==}
@@ -2036,9 +2024,6 @@ packages:
resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==}
engines: {node: '>=10'}
downloadjs@1.4.7:
resolution: {integrity: sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==}
dplayer@1.27.1:
resolution: {integrity: sha512-2laBMXs5V1B9zPwJ7eAIw/OBo+Xjvy03i4GHTk3Cg+IWbrq8rKMFO0fFr6ClAYotYOCcFGOvaJDkOZcgKllsCA==}
@@ -2313,7 +2298,7 @@ packages:
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
deprecated: Glob versions prior to v9 are no longer supported
globby@11.1.0:
resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
@@ -3322,9 +3307,6 @@ packages:
pretty-error@4.0.0:
resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==}
price-color@1.0.2:
resolution: {integrity: sha512-QkgrM0RxjAB+yxcRVuVTzgkxoYoGTAKbxIzJAZFFbActqKC4n9U4eDFWTpKQ+0FklRdZCnKenSPe+upfpe8+PQ==}
process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
@@ -3913,13 +3895,6 @@ packages:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
vue-awesome-swiper@4.1.1:
resolution: {integrity: sha512-50um10t6N+lJaORkpwSi1wWuMmBI1sgFc9Znsi5oUykw2cO5DzLaBHcO2JNX21R+Ue4TGoIJDhhxjBHtkFrTEQ==}
engines: {node: '>=8'}
peerDependencies:
swiper: ^5.2.0
vue: 2.x
vue-cli-plugin-style-resources-loader@0.1.5:
resolution: {integrity: sha512-LluhjWTZmpGl3tiXg51EciF+T70IN/9t6UvfmgluJBqxbrb6OV9i7L5lTd+OKtcTeghDkhcBmYhtTxxU4w/8sQ==}
@@ -3936,9 +3911,6 @@ packages:
peerDependencies:
vue: ^3.0.0
vue-json-excel@0.3.0:
resolution: {integrity: sha512-FrSh0tVUpw4K+ilLO8g0Qp52eFJw/hkk3rZPTEKo9qVkJgVfQtZwzj3UWc5ACYxA3jLk9HtjK+f9xKHCN4Kgag==}
vue-loader@15.11.1:
resolution: {integrity: sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==}
peerDependencies:
@@ -3986,10 +3958,6 @@ packages:
vue-template-es2015-compiler@1.9.1:
resolution: {integrity: sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==}
vue@2.7.16:
resolution: {integrity: sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==}
deprecated: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.
vue@3.5.39:
resolution: {integrity: sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==}
peerDependencies:
@@ -5608,14 +5576,6 @@ snapshots:
'@vue/compiler-core': 3.5.39
'@vue/shared': 3.5.39
'@vue/compiler-sfc@2.7.16':
dependencies:
'@babel/parser': 7.29.7
postcss: 8.5.15
source-map: 0.6.1
optionalDependencies:
prettier: 2.8.8
'@vue/compiler-sfc@3.5.39':
dependencies:
'@babel/parser': 7.29.7
@@ -6486,8 +6446,6 @@ snapshots:
dotenv@10.0.0: {}
downloadjs@1.4.7: {}
dplayer@1.27.1:
dependencies:
axios: 1.2.3
@@ -7694,11 +7652,6 @@ snapshots:
lodash: 4.18.1
renderkid: 3.0.0
price-color@1.0.2:
dependencies:
core-js: 3.49.0
vue: 2.7.16
process-nextick-args@2.0.1: {}
progress-webpack-plugin@1.0.16(webpack@5.108.4(postcss@8.5.15)):
@@ -8290,11 +8243,6 @@ snapshots:
vary@1.1.2: {}
vue-awesome-swiper@4.1.1(swiper@6.8.4)(vue@3.5.39):
dependencies:
swiper: 6.8.4
vue: 3.5.39
vue-cli-plugin-style-resources-loader@0.1.5: {}
vue-component-type-helpers@3.3.5: {}
@@ -8308,10 +8256,6 @@ snapshots:
'@vue/devtools-api': 6.6.4
vue: 3.5.39
vue-json-excel@0.3.0:
dependencies:
downloadjs: 1.4.7
vue-loader@15.11.1(@vue/compiler-sfc@3.5.39)(css-loader@6.11.0(webpack@5.108.4(postcss@8.5.15)))(lodash@4.18.1)(webpack@5.108.4(postcss@8.5.15)):
dependencies:
'@vue/component-compiler-utils': 3.3.0(lodash@4.18.1)
@@ -8404,11 +8348,6 @@ snapshots:
vue-template-es2015-compiler@1.9.1: {}
vue@2.7.16:
dependencies:
'@vue/compiler-sfc': 2.7.16
csstype: 3.2.3
vue@3.5.39:
dependencies:
'@vue/compiler-dom': 3.5.39

View File

@@ -43,7 +43,6 @@ export default {
watch: {
changed: {
handler(val) {
console.log(val,'changed')
this.$emit("selectedLink", val[0]); //因为是单选,所以直接返回第一个
},
deep: true

View File

@@ -1,117 +0,0 @@
// text
@prefixCls: zk-checkbox;
// color
@border: #dddee1;
@hoverBorder: #bcbcbc;
@blue: #2d8cf0;
.@{prefixCls}-wrapper {
display: flex;
justify-content: center;
}
.@{prefixCls} {
display: inline-block;
position: relative;
line-height: 1;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
outline: none;
&:hover {
.@{prefixCls}__inner {
border-color: @hoverBorder;
}
}
}
.@{prefixCls}__inner {
display: inline-block;
width: 14px;
height: 14px;
position: relative;
top: 0;
left: 0;
border: 1px solid @border;
border-radius: 2px;
background-color: #ffffff;
transition: border-color .2s ease-in-out,background-color .2s ease-in-out;
&::after {
content: "";
display: table;
width: 4px;
height: 8px;
position: absolute;
top: 1px;
left: 4px;
border: 2px solid #fff;
border-top: 0;
border-left: 0;
transform: rotate(45deg) scale(0);
transition: all .2s ease-in-out;
}
}
.@{prefixCls}--indeterminate {
.@{prefixCls}__inner {
background-color: @blue;
border-color: @blue;
&::after {
content: "";
width: 8px;
height: 1px;
transform: scale(1);
position: absolute;
left: 2px;
top: 5px;
}
}
&:hover {
.@{prefixCls}__inner {
border-color: @blue;
}
}
}
.@{prefixCls}--checked {
.@{prefixCls}__inner {
border-color: @blue;
background-color: @blue;
&::after {
content: "";
display: table;
width: 4px;
height: 8px;
position: absolute;
top: 1px;
left: 4px;
border: 2px solid #ffffff;
border-top: 0;
border-left: 0;
transform: rotate(45deg) scale(1);
transition: all .2s ease-in-out;
}
}
&:hover {
.@{prefixCls}__inner {
border-color: @blue;
}
}
}
.@{prefixCls}--disabled {
cursor: not-allowed;
.@{prefixCls}__inner {
background-color: #f3f3f3;
border-color: @border;
&::after {
animation-name: none;
border-color: #ccc;
}
}
&:hover {
.@{prefixCls}__inner {
border-color: @border;
}
}
}

View File

@@ -1,58 +0,0 @@
<template lang="html">
<label :class="`${prefixCls}-wrapper`">
<span :class="checkboxClass">
<span
:class="`${prefixCls}__inner`"
@click="toggle"></span>
</span>
</label>
</template>
<script>
export default {
name: 'zk-checkbox',
props: {
value: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
indeterminate: {
type: Boolean,
default: false,
},
},
data() {
return {
prefixCls: 'zk-checkbox',
};
},
computed: {
checkboxClass() {
return [
`${this.prefixCls}`,
{
[`${this.prefixCls}--disabled`]: this.disabled,
[`${this.prefixCls}--checked`]: this.value,
[`${this.prefixCls}--indeterminate`]: this.indeterminate,
},
];
},
},
methods: {
toggle() {
if (this.disabled) {
return false;
}
const value = !this.value;
this.$emit('input', value);
return this.$emit('on-change', value);
},
},
};
</script>
<style lang="less" scoped src="./Checkbox.less"></style>

View File

@@ -1,170 +0,0 @@
@import "./font/iconfont";
// text
@prefixCls: zk-table;
// color
@black: #515a6e;
@white: #ffffff;
@border: #e9eaec;
@hoverRow: #ebf7ff;
@backgroundRow: #f8f8f9;
.@{prefixCls} {
position: relative;
width: 100%;
box-sizing: border-box;
background-color: @white;
border: 1px solid @border;
font-size: 14px;
line-height: 26px;
color: @black;
overflow: hidden;
}
.@{prefixCls}__header-cell {
font-weight: bold;
}
.@{prefixCls}__cell-inner {
padding: 0px 18px;
}
.@{prefixCls}-default {
font-size: 14px;
.@{prefixCls}__header-row {
height: 40px !important;
}
.@{prefixCls}__body-row {
height: 48px !important;
}
}
.@{prefixCls}-small {
font-size: 12px;
.@{prefixCls}__header-row {
height: 36px !important;
}
.@{prefixCls}__body-row {
height: 40px !important;
}
}
.@{prefixCls}-large {
font-size: 16px;
.@{prefixCls}__header-row {
height: 43px !important;
}
.@{prefixCls}__body-row {
height: 60px !important;
}
}
.@{prefixCls}__header-wrapper,
.@{prefixCls}__footer-wrapper {
overflow: hidden;
}
.@{prefixCls}__body-wrapper {
overflow: auto;
}
.@{prefixCls}__header,
.@{prefixCls}__body,
.@{prefixCls}__footer {
width: 100%;
table-layout: fixed;
border-collapse: collapse;
border-spacing: 0;
}
.@{prefixCls}__header-row {
height: 40px;
box-sizing: border-box;
background-color: @backgroundRow;
border-bottom: 1px solid @border;
}
.@{prefixCls}__footer-row {
height: 40px;
box-sizing: border-box;
background-color: @white;
border-top: 1px solid @border;
}
.@{prefixCls}__body-row {
height: 48px;
box-sizing: border-box;
&:not(:first-of-type) {
border-top: 1px solid @border;
}
}
.@{prefixCls}__header-cell,
.@{prefixCls}__body-cell,
.@{prefixCls}__footer-cell {
box-sizing: border-box;
text-align: left;
vertical-align: middle;
word-break: break-all;
overflow: hidden;
}
.@{prefixCls}--firstProp-header-inner {
padding-left: 32px;
}
.@{prefixCls}--empty-row {
height: 80px;
}
.@{prefixCls}--empty-content {
text-align: center;
}
.@{prefixCls}--center-cell {
text-align: center;
}
.@{prefixCls}--right-cell {
text-align: right;
}
.@{prefixCls}--stripe-row {
background-color: @backgroundRow;
}
.@{prefixCls}--row-hover {
background-color: @hoverRow;
}
.@{prefixCls}--border-cell {
&:not(:last-of-type) {
border-right: 1px solid @border;
}
}
.@{prefixCls}--tree-icon {
margin-right: 6px;
cursor: pointer;
}
.@{prefixCls}--expand-inner {
text-align: center;
cursor: pointer;
transition: transform .2s ease-in-out;
}
.@{prefixCls}--expanded-inner {
transform: rotate(90deg);
}
.@{prefixCls}--expand-content {
padding: 20px;
}

View File

@@ -1,401 +0,0 @@
<template lang="html">
<div
v-if="columns.length > 0"
ref="table"
v-loading="loading"
:class="[prefixCls, `${prefixCls}-${size}`, tableClass]">
<div
v-show="showHeader"
ref="header-wrapper"
:class="`${prefixCls}__header-wrapper`"
@mousewheel="handleEvent('header', $event)">
<table-header
ref="table-header">
</table-header>
</div>
<div
ref="body-wrapper"
:style="bodyWrapperStyle"
:class="`${prefixCls}__body-wrapper`"
@scroll="handleEvent('body', $event)">
<table-body
ref="table-body"
:class="bodyClass">
</table-body>
</div>
<div
v-show="showSummary && data.length > 0"
ref="footer-wrapper"
:class="`${prefixCls}__footer-wrapper`"
@mousewheel="handleEvent('footer', $event)">
<table-footer
ref="table-footer">
</table-footer>
</div>
</div>
</template>
<script>
import TableHeader from "./TableHeader";
import TableBody from "./TableBody";
import TableFooter from "./TableFooter";
import { mixins, scrollBarWidth as getSbw } from "./utils";
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-param-reassign */
// function getBodyData(data, isTreeType, childrenProp, isFold, level = 1) {
function getBodyData(
primaryKey,
oldBodyData,
data,
isTreeType,
childrenProp,
isFold,
parentFold,
level = 1
) {
let bodyData = [];
data.forEach((row, index) => {
const children = row[childrenProp];
const childrenLen =
Object.prototype.toString.call(children).slice(8, -1) === "Array"
? children.length
: 0;
let curIsFold = isFold;
if (
isFold &&
typeof primaryKey === "string" &&
Array.isArray(oldBodyData)
) {
for (let i = 0; i < oldBodyData.length; i++) {
const oldRow = oldBodyData[i];
if (oldRow[primaryKey] === row[primaryKey]) {
if ("_isFold" in oldRow) {
curIsFold = oldRow._isFold;
}
break;
}
}
}
bodyData.push({
_isHover: false,
_isExpanded: false,
_isChecked: false,
_level: level,
// _isHide: isFold ? level !== 1 : false,
// _isFold: isFold,
_isHide: level !== 1 ? isFold && parentFold : false,
_isFold: isFold && curIsFold,
_childrenLen: childrenLen,
_normalIndex: index + 1,
...row
});
if (isTreeType) {
if (childrenLen > 0) {
// bodyData = bodyData.concat(getBodyData(children, true, childrenProp, isFold, level + 1));
bodyData = bodyData.concat(
getBodyData(
primaryKey,
oldBodyData,
children,
true,
childrenProp,
isFold,
curIsFold,
level + 1
)
);
}
}
});
return bodyData;
}
function initialState(table, expandKey) {
return {
bodyHeight: "auto",
firstProp: expandKey || (table.columns[0] && table.columns[0].key),
// bodyData: getBodyData(table.data, table.treeType, table.childrenProp, table.isFold),
bodyData: getBodyData(
table.primaryKey,
table.bodyData,
table.data,
table.treeType,
table.childrenProp,
table.isFold,
false
)
};
}
function initialColumns(table, clientWidth) {
let columnsWidth = 0;
const minWidthColumns = [];
const otherColumns = [];
const columns = table.columns.concat();
if (table.expandType) {
columns.unshift({
width: "50"
});
}
if (table.selectable) {
columns.unshift({
width: "50"
});
}
if (table.showIndex) {
columns.unshift({
width: "50px",
key: "_normalIndex",
title: table.indexText
});
}
columns.forEach((column, index) => {
let width = "";
let minWidth = "";
if (!column.width) {
if (column.minWidth) {
minWidth =
typeof column.minWidth === "number"
? column.minWidth
: parseInt(column.minWidth, 10);
} else {
minWidth = 80;
}
minWidthColumns.push({
...column,
minWidth,
_index: index
});
} else {
width =
typeof column.width === "number"
? column.width
: parseInt(column.width, 10);
otherColumns.push({
...column,
width,
_index: index
});
}
columnsWidth += minWidth || width;
});
const scrollBarWidth = getSbw();
const totalWidth = columnsWidth + scrollBarWidth;
const isScrollX = totalWidth > clientWidth;
if (!isScrollX) {
const extraWidth = clientWidth - totalWidth;
const averageExtraWidth = Math.floor(extraWidth / minWidthColumns.length);
minWidthColumns.forEach(column => {
column.computedWidth = column.minWidth + averageExtraWidth;
});
}
const tableColumns = otherColumns.concat(minWidthColumns);
tableColumns.sort((a, b) => a._index - b._index);
return tableColumns;
}
export default {
name: "TreeTable",
mixins: [mixins],
components: {
TableHeader,
TableBody,
TableFooter
},
props: {
data: {
type: Array,
default: () => []
},
columns: {
type: Array,
default: () => []
},
size: {
default() {
return !this.$IVIEW || this.$IVIEW.size === ""
? "default"
: this.$IVIEW.size;
}
},
loading: {
type: Boolean,
default: false
},
maxHeight: {
type: [String, Number],
default: "auto"
},
stripe: {
type: Boolean,
default: false
},
border: {
type: Boolean,
default: false
},
treeType: {
type: Boolean,
default: true
},
childrenProp: {
type: String,
default: "children"
},
isFold: {
type: Boolean,
default: true
},
expandType: {
type: Boolean,
default: true
},
selectable: {
type: Boolean,
default: true
},
selectType: {
type: String,
default: "checkbox"
},
emptyText: {
type: String,
default: "暂无数据"
},
showHeader: {
type: Boolean,
default: true
},
showIndex: {
type: Boolean,
default: false
},
indexText: {
type: String,
default: "#"
},
showSummary: {
type: Boolean,
default: false
},
sumText: {
type: String,
default: "合计"
},
primaryKey: String,
summaryMethod: Function,
showRowHover: {
type: Boolean,
default: true
},
rowKey: Function,
rowClassName: [String, Function],
cellClassName: [String, Function],
rowStyle: [Object, Function],
cellStyle: [Object, Function],
expandKey: String
},
provide() {
return {
treeTableRoot: this,
};
},
data() {
return {
computedWidth: "",
computedHeight: "",
tableColumns: [],
...initialState(this, this.expandKey)
};
},
computed: {
bodyWrapperStyle() {
return {
height: this.bodyHeight
};
},
tableClass() {
return {
[`${this.prefixCls}--border`]: this.border
};
},
bodyClass() {
return {
[`${this.prefixCls}--stripe`]: this.stripe
};
}
},
methods: {
handleEvent(type, $event) {
this.validateType(type, ["header", "body", "footer"], "handleEvent");
const eventType = $event.type;
if (eventType === "scroll") {
this.$refs["header-wrapper"].scrollLeft = $event.target.scrollLeft;
this.$refs["footer-wrapper"].scrollLeft = $event.target.scrollLeft;
}
if (eventType === "mousewheel") {
const deltaX = $event.deltaX;
const $body = this.$refs["body-wrapper"];
if (deltaX > 0) {
$body.scrollLeft += 10;
} else {
$body.scrollLeft -= 10;
}
}
return this.$emit(`${type}-${eventType}`, $event);
},
// computedWidth, computedHeight, tableColumns
measure() {
this.$nextTick(() => {
const { clientWidth, clientHeight } = this.$el;
this.computedWidth = clientWidth + 2;
this.computedHeight = clientHeight + 2;
const maxHeight = parseInt(this.maxHeight, 10);
if (this.maxHeight !== "auto" && this.computedHeight > maxHeight) {
this.bodyHeight = `${maxHeight - 83}px`;
}
this.tableColumns = initialColumns(this, clientWidth);
});
},
getCheckedProp(key = "index") {
if (!this.selectable) {
return [];
}
const checkedIndexs = [];
this.bodyData.forEach((item, index) => {
if (item._isChecked) {
if (key === "index") {
checkedIndexs.push(index);
} else {
checkedIndexs.push(item[key]);
}
}
});
return checkedIndexs;
}
},
watch: {
$props: {
deep: true,
handler() {
Object.assign(this.$data, initialState(this, this.expandKey));
}
}
},
updated() {
this.measure();
},
mounted() {
this.measure();
window.addEventListener("resize", this.measure);
},
beforeUnmount() {
window.removeEventListener("resize", this.measure);
}
};
</script>
<style lang="less" src="./Table.less"></style>

View File

@@ -1,355 +0,0 @@
import Checkbox from '../Checkbox/Checkbox'; // eslint-disable-line
// import Radio from '../Radio/Radio'; // eslint-disable-line
import { mixins } from './utils';
/* eslint-disable no-underscore-dangle */
export default {
name: 'TreeTable__body',
mixins: [mixins],
inject: {
treeTableRoot: { default: null },
},
data() {
return {
radioSelectedIndex: -1,
};
},
computed: {
table() {
if (this.treeTableRoot) return this.treeTableRoot;
let parent = this.$parent;
while (parent) {
if (parent.$options && parent.$options.name === 'TreeTable') return parent;
parent = parent.$parent;
}
return this.$parent;
},
},
methods: {
toggleStatus(type, row, rowIndex, value) {
this.validateType(type, ['Expanded', 'Checked', 'Hide', 'Fold'], 'toggleStatus', false);
const target = this.table.bodyData[rowIndex];
this.table.bodyData.splice(rowIndex, 1, {
...target,
[`_is${type}`]: typeof value === 'undefined' ? !row[`_is${type}`] : value,
});
},
getChildrenIndex(parentLevel, parentIndex, careFold = true) {
const data = this.table.bodyData;
let childrenIndex = [];
for (let i = parentIndex + 1; i < data.length; i++) {
if (data[i]._level <= parentLevel) break;
if (data[i]._level - 1 === parentLevel) {
childrenIndex.push(i);
}
}
const len = childrenIndex.length; // important!!!
if (len > 0) {
for (let i = 0; i < len; i++) {
const childData = data[childrenIndex[i]];
if (
childData._childrenLen &&
(!careFold || (careFold && !childData._isFold))
) {
childrenIndex = childrenIndex.concat(
this.getChildrenIndex(childData._level, childrenIndex[i], careFold));
}
}
}
return childrenIndex;
},
handleEvent($event, type, data, others) {
const certainType = this.validateType(type, ['cell', 'row', 'checkbox', 'icon', 'radio'], 'handleEvent');
const eventType = $event ? $event.type : '';
const { row, rowIndex, column, columnIndex } = data;
const latestData = this.table.bodyData;
// Checkbox
if (certainType.checkbox) {
const { isChecked } = others;
this.toggleStatus('Checked', row, rowIndex, isChecked);
if (row._childrenLen > 0) {
const childrenIndex = this.getChildrenIndex(row._level, rowIndex, false);
for (let i = 0; i < childrenIndex.length; i++) {
this.toggleStatus('Checked', latestData[childrenIndex[i]], childrenIndex[i], isChecked);
}
}
return this.table.$emit('checkbox-click', latestData[rowIndex], column, columnIndex, $event);
}
// Radio
if (certainType.radio) {
this.radioSelectedIndex = rowIndex;
return this.table.$emit('radio-click', { row, rowIndex, column, columnIndex, $event });
}
// Tree's icon
if (certainType.icon) {
$event.stopPropagation();
this.toggleStatus('Fold', row, rowIndex);
const childrenIndex = this.getChildrenIndex(row._level, rowIndex);
for (let i = 0; i < childrenIndex.length; i++) {
this.toggleStatus('Hide', latestData[childrenIndex[i]], childrenIndex[i]);
}
return this.table.$emit('tree-icon-click', latestData[rowIndex], column, columnIndex, $event);
}
if (certainType.cell && eventType === 'click') {
// 点击扩展单元格
if (this.isExpandCell(this.table, columnIndex)) {
this.toggleStatus('Expanded', row, rowIndex);
return this.table.$emit('expand-cell-click', latestData[rowIndex], column, columnIndex, $event);
}
}
// 行:Hover
if (certainType.row && (eventType === 'mouseenter' || eventType === 'mouseleave')) {
const { hover } = others;
const target = latestData[rowIndex];
latestData.splice(rowIndex, 1, {
...target,
_isHover: hover,
});
}
if (certainType.row && others && others.clickRow && certainType.radio) {
this.radioSelectedIndex = rowIndex;
return this.table.$emit('radio-click', { row, rowIndex, column, columnIndex, $event });
}
if (certainType.cell) {
return this.table.$emit(`${type}-${eventType}`, latestData[rowIndex], rowIndex, column, columnIndex, $event);
}
return this.table.$emit(`${type}-${eventType}`, latestData[rowIndex], rowIndex, $event);
},
},
render() {
// key
// function getKey(row, rowIndex) {
// const rowKey = this.table.rowKey;
// if (rowKey) {
// return rowKey.call(null, row, rowIndex);
// }
// return rowIndex;
// }
// style
function getStyle(type, row, rowIndex, column, columnIndex) {
const certainType = this.validateType(type, ['cell', 'row'], 'getStyle');
const style = this.table[`${type}Style`];
if (typeof style === 'function') {
if (certainType.row) {
return style.call(null, row, rowIndex);
}
if (certainType.cell) {
return style.call(null, row, rowIndex, column, columnIndex);
}
}
return style;
}
// className
function getClassName(type, row, rowIndex, column, columnIndex) {
const certainType = this.validateType(type, ['cell', 'row', 'inner'], 'getClassName');
const classList = [];
if (column && column.key == "_normalIndex") {
classList.push(`${this.prefixCls}--center-cell`);
}
// console.log(certainType.inner)
if (certainType.row || certainType.cell) {
const className = this.table[`${type}ClassName`];
if (typeof className === 'string') {
classList.push(className);
} else if (typeof className === 'function') {
if (certainType.row) {
classList.push(className.call(null, row, rowIndex) || '');
}
if (certainType.cell) {
classList.push(className.call(null, row, rowIndex, column, columnIndex) || '');
}
}
if (certainType.row) {
classList.push(`${this.prefixCls}__body-row`);
if (this.table.stripe && rowIndex % 2 !== 0) {
classList.push(`${this.prefixCls}--stripe-row`);
}
if (this.table.showRowHover && row._isHover) {
classList.push(`${this.prefixCls}--row-hover`);
}
}
if (certainType.cell) {
classList.push(`${this.prefixCls}__body-cell`);
if (this.table.border) {
classList.push(`${this.prefixCls}--border-cell`);
}
const align = column.align;
if (['center', 'right'].indexOf(align) > -1) {
classList.push(`${this.prefixCls}--${align}-cell`);
}
}
}
if (certainType.inner) {
classList.push(`${this.prefixCls}__cell-inner`);
if (this.isExpandCell(this.table, columnIndex)) {
classList.push(`${this.prefixCls}--expand-inner`);
if (row._isExpanded) {
classList.push(`${this.prefixCls}--expanded-inner`);
}
}
}
return classList.join(' ');
}
// Vue 3:scoped slot 合并到 $slots
function renderTemplateSlot(table, slotName, scope) {
if (!table || !slotName) return '';
const slots = table.$slots || {};
let slot = slots[slotName];
if (!slot && table.$scopedSlots && table.$scopedSlots[slotName]) {
slot = table.$scopedSlots[slotName];
}
if (!slot) return '';
return slot(scope);
}
// 根据type渲染单元格Cell
function renderCell(row, rowIndex, column, columnIndex) {
if (!row || !column) return '';
// ExpandType
if (this.isExpandCell(this.table, columnIndex)) {
return <i class='zk-icon zk-icon-angle-right'></i>;
}
// SelectionType's Checkbox
if (this.isSelectionCell(this.table, columnIndex)) {
let res = null;
if (this.table.selectType === 'checkbox') {
let allCheck;
let childrenIndex;
const hasChildren = row._childrenLen > 0;
if (hasChildren) {
childrenIndex = this.getChildrenIndex(row._level, rowIndex, false);
allCheck = true;
for (let i = 0; i < childrenIndex.length; i++) {
if (!this.table.bodyData[childrenIndex[i]]._isChecked) {
allCheck = false;
break;
}
}
} else {
allCheck = row._isChecked;
}
let indeterminate = false;
if (hasChildren && !allCheck) {
for (let i = 0; i < childrenIndex.length; i++) {
if (this.table.bodyData[childrenIndex[i]]._isChecked) {
indeterminate = true;
break;
}
}
}
// res = <Checkbox
// indeterminate={indeterminate}
// value={allCheck}
// onOn-change={isChecked => this.handleEvent(null, 'checkbox', { row, rowIndex, column, columnIndex }, { isChecked })}>
// </Checkbox>;
} else {
res = (
<input
type="radio"
checked={this.radioSelectedIndex === rowIndex}
onChange={() =>
this.handleEvent(null, 'radio', { row, rowIndex, column, columnIndex })
}
/>
);
}
return res;
}
// Tree's firstProp
if (this.table.treeType && this.table.firstProp === column.key) {
return <span
class={`${this.prefixCls}--level-${row._level}-cell`}
style={{
marginLeft: `${(row._level - 1) * 24}px`,
paddingLeft: row._childrenLen === 0 ? '20px' : '',
}}>
{row._childrenLen > 0 &&
<i
class={`${this.prefixCls}--tree-icon zk-icon zk-icon-${row._isFold ? 'plus' : 'minus'}-square-o`}
on-click={$event => this.handleEvent($event, 'icon', { row, rowIndex, column, columnIndex }, { isFold: row._isFold })}></i>
}
{row[column.key] ? row[column.key] : ''}
</span>;
}
// TreeType children's index
if (this.table.showIndex && this.table.treeType && column.key === '_normalIndex' && row._level > 1) {
return '';
}
if (column.type === undefined || column.type === 'custom') {
return row[column.key];
} else if (column.type === 'template') {
return renderTemplateSlot.call(this, this.table, column.template, {
row,
rowIndex,
column,
columnIndex,
});
}
return '';
}
// Template
return (
<table cellspacing="0" cellpadding="0" border="0" class={`${this.prefixCls}__body`}>
{/* <colgroup>
{this.table.tableColumns.map(column =>
<col width={column.computedWidth || column.minWidth || column.width}></col>)
}
</colgroup> */}
<tbody>
{ this.table.bodyData.length > 0
? this.table.bodyData.map((row, rowIndex) =>
[
<tr
v-show={!row._isHide}
key={`table_row_${rowIndex}`}
style={getStyle.call(this, 'row', row, rowIndex)}
class={getClassName.call(this, 'row', row, rowIndex)}
on-click={$event => this.handleEvent($event, 'row', { row, rowIndex }, { clickRow: true })}
on-dblclick={$event => this.handleEvent($event, 'row', { row, rowIndex })}
on-contextmenu={$event => this.handleEvent($event, 'row', { row, rowIndex })}
on-mouseenter={$event => this.handleEvent($event, 'row', { row, rowIndex }, { hover: true })}
on-mouseleave={$event => this.handleEvent($event, 'row', { row, rowIndex }, { hover: false })}>
{ this.table.tableColumns.map((column, columnIndex) =>
column.key ? <td
style={getStyle.call(this, 'cell', row, rowIndex, column, columnIndex)}
class={getClassName.call(this, 'cell', row, rowIndex, column, columnIndex)}
on-click={$event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex })}
on-dblclick={$event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex })}
on-contextmenu={$event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex })}
on-mouseenter={$event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex })}
on-mouseleave={$event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex })}>
<div class={getClassName.call(this, 'inner', row, rowIndex, column, columnIndex)}>
{renderCell.call(this, row, rowIndex, column, columnIndex)}
</div>
</td>:"")
}
</tr>,
this.table.expandType && row._isExpanded &&
<tr
key={rowIndex}
class={`${this.prefixCls}__body-row ${this.prefixCls}--expand-row`}>
<td
class={`${this.prefixCls}--expand-content`}
colspan={this.table.tableColumns.length}>
{renderTemplateSlot.call(this, this.table, 'expand', { row, rowIndex })}
</td>
</tr>,
])
: <tr
class={`${this.prefixCls}--empty-row`}>
<td
class={`${this.prefixCls}__body-cell ${this.prefixCls}--empty-content`}
colspan={this.table.tableColumns.length}>
{this.table.emptyText}
</td>
</tr>
}
</tbody>
</table>
);
},
};

View File

@@ -1,84 +0,0 @@
import { mixins } from './utils';
/* eslint-disable no-underscore-dangle */
export default {
name: 'TreeTable__footer',
mixins: [mixins],
data() {
return {
};
},
computed: {
table() {
return this.$parent;
},
},
methods: {
},
render() {
// 计算各列总和
function renderCell({ key }, columnIndex) {
if (columnIndex === 0) {
return this.table.sumText;
}
const rows = this.table.bodyData;
const values = rows.map(row => Number(row[key]));
const precisions = [];
let notNumber = true;
values.forEach((value) => {
if (!isNaN(value)) {
notNumber = false;
const decimal = value.toString().split('.')[1];
precisions.push(decimal ? decimal.length : 0);
}
});
const precision = Math.max.apply(null, precisions);
if (!notNumber) {
return values.reduce((prev, curr) => {
const value = Number(curr);
if (!isNaN(value)) {
return parseFloat((prev + curr).toFixed(precision));
}
return prev;
}, 0);
}
return '';
}
// className
function getClassName() {
const classList = [];
classList.push(`${this.prefixCls}__footer-cell`);
if (this.table.border) {
classList.push(`${this.prefixCls}--border-cell`);
}
return classList.join(' ');
}
// Template
return (
<table cellspacing="0" cellpadding="0" border="0" class={ `${this.prefixCls}__footer` }>
<colgroup>
{ this.table.tableColumns.map(column =>
<col width={ column.computedWidth || column.minWidth || column.width }></col>)
}
</colgroup>
<tfoot>
<tr class={ `${this.prefixCls}__footer-row` }>
{ this.table.tableColumns.map((column, columnIndex) =>
<td class={ getClassName.call(this) }>
<div class={ `${this.prefixCls}__cell-inner` }>
{ this.table.summaryMethod
? this.table.summaryMethod(this.table.bodyData, column, columnIndex)
: renderCell.call(this, column, columnIndex) }
</div>
</td>)
}
</tr>
</tfoot>
</table>
);
},
};

View File

@@ -1,108 +0,0 @@
import Checkbox from "../Checkbox/Checkbox"; // eslint-disable-line
import { mixins } from "./utils";
/* eslint-disable no-underscore-dangle */
export default {
name: "TreeTable__header",
mixins: [mixins],
data() {
return {};
},
computed: {
table() {
return this.$parent;
}
},
methods: {
toggleAllChecked(checked) {
this.table.bodyData = this.table.bodyData.map(row => ({
...row,
_isChecked: checked
}));
}
},
render() {
// className
function getClassName(type, { headerAlign, key }) {
const certainType = this.validateType(
type,
["cell", "inner"],
"getClassName"
);
const classList = [];
if (key == "_normalIndex") {
classList.push(`${this.prefixCls}--center-cell`);
}
if (certainType.cell) {
classList.push(`${this.prefixCls}__header-cell`);
if (this.table.border) {
classList.push(`${this.prefixCls}--border-cell`);
}
if (["center", "right"].indexOf(headerAlign) > -1) {
classList.push(`${this.prefixCls}--${headerAlign}-cell`);
}
}
if (certainType.inner) {
classList.push(`${this.prefixCls}__cell-inner`);
if (this.table.treeType && this.table.firstProp === key) {
classList.push(`${this.prefixCls}--firstProp-header-inner`);
}
}
return classList.join(" ");
}
// 根据type渲染单元格Label
function renderLabel(column, columnIndex) {
if (
this.isSelectionCell(this.table, columnIndex) &&
this.selectType === "checkbox"
) {
const allCheck = this.table.bodyData.every(row => row._isChecked);
const indeterminate =
!allCheck && this.table.bodyData.some(row => row._isChecked);
return (
<Checkbox
indeterminate={indeterminate}
value={allCheck}
onOn-change={checked => this.toggleAllChecked(checked)}
></Checkbox>
);
}
return column.title ? column.title : "";
}
// Template
return (
<table
cellspacing="0"
cellpadding="0"
border="0"
class={`${this.prefixCls}__header`}
>
{/* <colgroup>
{this.table.tableColumns.map(column => (
<col
width={column.computedWidth || column.minWidth || column.width}
></col>
))}
</colgroup> */}
<thead>
<tr class={`${this.prefixCls}__header-row`}>
{
this.table.tableColumns.map((column, columnIndex) =>
column.key ? (
<th class={getClassName.call(this, "cell", column)}>
<div class={getClassName.call(this, "inner", column)}>
{renderLabel.call(this, column, columnIndex)}
</div>
</th>
) : (
""
)
)}
</tr>
</thead>
</table>
);
}
};

View File

@@ -1,22 +0,0 @@
// icon
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1505310522875'); /* IE9*/
src: url('iconfont.eot?t=1505310522875#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAW0AAsAAAAACOQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW7kggY21hcAAAAYAAAABuAAABojLtBtFnbHlmAAAB8AAAAa8AAAKA73SzT2hlYWQAAAOgAAAALwAAADYO3fRqaGhlYQAAA9AAAAAcAAAAJAfeA4ZobXR4AAAD7AAAABMAAAAUE+kAAGxvY2EAAAQAAAAADAAAAAwBbAHYbWF4cAAABAwAAAAeAAAAIAEUAF1uYW1lAAAELAAAAUUAAAJtPlT+fXBvc3QAAAV0AAAAQAAAAFryy5h0eJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2Bk/s04gYGVgYOpk+kMAwNDP4RmfM1gxMjBwMDEwMrMgBUEpLmmMDgwVDwzZm7438AQw9zA0AAUZgTJAQAoHgyieJzFkcENwyAUQ98HyqHKKDmEZoEOklOnYOK/RmI+uXSCWDLG/pZAALyALK5iAfthDBxKLfLMO/LCJl+lRqL7fp7y3VuoKprV0KxO0qbyGOy5o/+xxPq9nV6YflNX9DY5fsA/k6Pj+yTpAn3jEO8AAHiclVDNitNQFD7n3slNE9vE5N7kpOn0J0mbKB3DGDMZRGw3bhQXA2LB5TyAbmfjohvBhQvfYEAEoc8wr+EDiK4KPkITU0EcXDmHw3fOgfN9fHygATTf+BUPQMIduA9P4AwAxRxjiw0xysqczdGLNI+UxbMki/QkzvljpFgov6jKlIQubLRwhA+iospyluFJuWCPsPCHiP1B+MKdHbr8I5pBNnpXP2Of0Bsnh/biXv30aKmKiexcdF2377ofOkLTOowd2Ba+Jt/QDFPUnzU79K7Gd9kYu/0sfP6qNxm45+/LN8MZGYjrNcrBxPqydEKn7behL92+frvXCcJeMlV48eNWILvD9Du0hXtgl+wSHIBZnJZLzNKyKgh9paPAdVca260hAxPBMBowz9t1uzUDuT8CswEDDtq8Gr7mADaM4QgetrKRhbozQooWeOrkKJVIojg9ccqqjcT3uBJ6lGNZnUYjnBU+ORbGaeYskM93m+kx4vGUrX5PQXK3kUSSrSS9JFWvFJHCjaIGJCFSugcOLeM6c7f6wyFZf/37+PO6AgD/x/vNnN/A7H8bhF86tmcbAHicY2BkYGAAYl/p/jnx/DZfGbhZGEDg6v0IKwT9/yELA7MEkMvBwAQSBQAZYgnZAHicY2BkYGBu+N/AEMPCAAJAkpEBFbACAEcLAm54nGNhYGBgfsnAwMKAwAAOmwD9AAAAAAAAdgCYAPYBQHicY2BkYGBgZQgEYhBgAmIuIGRg+A/mMwAAES0BcgAAeJxlj01OwzAQhV/6B6QSqqhgh+QFYgEo/RGrblhUavdddN+mTpsqiSPHrdQDcB6OwAk4AtyAO/BIJ5s2lsffvHljTwDc4Acejt8t95E9XDI7cg0XuBeuU38QbpBfhJto41W4Rf1N2MczpsJtdGF5g9e4YvaEd2EPHXwI13CNT+E69S/hBvlbuIk7/Aq30PHqwj7mXle4jUcv9sdWL5xeqeVBxaHJIpM5v4KZXu+Sha3S6pxrW8QmU4OgX0lTnWlb3VPs10PnIhVZk6oJqzpJjMqt2erQBRvn8lGvF4kehCblWGP+tsYCjnEFhSUOjDFCGGSIyujoO1Vm9K+xQ8Jee1Y9zed0WxTU/3OFAQL0z1xTurLSeTpPgT1fG1J1dCtuy56UNJFezUkSskJe1rZUQuoBNmVXjhF6XNGJPyhnSP8ACVpuyAAAAHicY2BigAAuBuyAlZGJkZmRhZGVkY2BsYI7MS89J1W3KDM9o4S3IKe0WLe4sDSxKFU3ny83Mw+Jy8AAAHSWD8A=') format('woff'),
url('iconfont.ttf?t=1505310522875') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('iconfont.svg?t=1505310522875#iconfont') format('svg'); /* iOS 4.1- */
}
.zk-icon {
font-family: "iconfont" !important;
font-size: 14px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.zk-icon-plus-square-o:before { content: "\e631"; }
.zk-icon-minus-square-o:before { content: "\e632"; }
.zk-icon-angle-right:before { content: "\e633"; }

View File

@@ -1,42 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
<font-face
font-family="iconfont"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
ascent="896"
descent="-128"
/>
<missing-glyph />
<glyph glyph-name="x" unicode="x" horiz-adv-x="1001"
d="M281 543q-27 -1 -53 -1h-83q-18 0 -36.5 -6t-32.5 -18.5t-23 -32t-9 -45.5v-76h912v41q0 16 -0.5 30t-0.5 18q0 13 -5 29t-17 29.5t-31.5 22.5t-49.5 9h-133v-97h-438v97zM955 310v-52q0 -23 0.5 -52t0.5 -58t-10.5 -47.5t-26 -30t-33 -16t-31.5 -4.5q-14 -1 -29.5 -0.5
t-29.5 0.5h-32l-45 128h-439l-44 -128h-29h-34q-20 0 -45 1q-25 0 -41 9.5t-25.5 23t-13.5 29.5t-4 30v167h911zM163 247q-12 0 -21 -8.5t-9 -21.5t9 -21.5t21 -8.5q13 0 22 8.5t9 21.5t-9 21.5t-22 8.5zM316 123q-8 -26 -14 -48q-5 -19 -10.5 -37t-7.5 -25t-3 -15t1 -14.5
t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-5 19 -11 39h-368zM336 498v228q0 11 2.5 23t10 21.5t20.5 15.5t34 6h188q31 0 51.5 -14.5t20.5 -52.5v-227h-327z" />
<glyph glyph-name="angle-right" unicode="&#58931;" d="M384.087 97.474c-7.857 0-15.713 2.997-21.707 8.992-11.989 11.989-11.989 31.426 0 43.415l234.118 234.12-234.118 234.118c-11.988 11.989-11.988 31.427 0 43.416 11.989 11.988 31.426 11.988 43.416 0l255.826-255.827c11.989-11.989 11.989-31.427 0-43.416l-255.826-255.827c-5.995-5.995-13.851-8.992-21.708-8.992z" horiz-adv-x="1024" />
<glyph glyph-name="plus-square-o" unicode="&#58929;" d="M810.666667 768H213.333333c-46.933333 0-85.333333-38.4-85.333333-85.333333v-597.333334c0-46.933333 38.4-85.333333 85.333333-85.333333h597.333334c46.933333 0 85.333333 38.4 85.333333 85.333333V682.666667c0 46.933333-38.4 85.333333-85.333333 85.333333z m42.666666-682.666667c0-25.6-17.066667-42.666667-42.666666-42.666666H213.333333c-25.6 0-42.666667 17.066667-42.666666 42.666666V682.666667c0 25.6 17.066667 42.666667 42.666666 42.666666h597.333334c25.6 0 42.666667-17.066667 42.666666-42.666666v-597.333334zM768 384c0-25.6-17.066667-42.666667-42.666667-42.666667H298.666667c-25.6 0-42.666667 17.066667-42.666667 42.666667s17.066667 42.666667 42.666667 42.666667h426.666666c25.6 0 42.666667-17.066667 42.666667-42.666667zM512 640c25.6 0 42.666667-17.066667 42.666667-42.666667v-426.666666c0-25.6-17.066667-42.666667-42.666667-42.666667s-42.666667 17.066667-42.666667 42.666667V597.333333c0 25.6 17.066667 42.666667 42.666667 42.666667z" horiz-adv-x="1024" />
<glyph glyph-name="minus-square-o" unicode="&#58930;" d="M810.666667 768H213.333333c-46.933333 0-85.333333-38.4-85.333333-85.333333v-597.333334c0-46.933333 38.4-85.333333 85.333333-85.333333h597.333334c46.933333 0 85.333333 38.4 85.333333 85.333333V682.666667c0 46.933333-38.4 85.333333-85.333333 85.333333z m42.666666-682.666667c0-25.6-17.066667-42.666667-42.666666-42.666666H213.333333c-25.6 0-42.666667 17.066667-42.666666 42.666666V682.666667c0 25.6 17.066667 42.666667 42.666666 42.666666h597.333334c25.6 0 42.666667-17.066667 42.666666-42.666666v-597.333334zM768 384c0-25.6-17.066667-42.666667-42.666667-42.666667H298.666667c-25.6 0-42.666667 17.066667-42.666667 42.666667s17.066667 42.666667 42.666667 42.666667h426.666666c25.6 0 42.666667-17.066667 42.666667-42.666667z" horiz-adv-x="1024" />
</font>
</defs></svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1,6 +0,0 @@
import mixins from './mixins';
import scrollBarWidth from './scrollBarWidth';
export {
mixins,
scrollBarWidth
}

View File

@@ -1,34 +0,0 @@
export default {
data() {
return {
prefixCls: 'zk-table',
};
},
methods: {
validateType(type, validTypes, funcName, isReturn = true) {
if (validTypes.indexOf(type) < 0) throw new Error(`${funcName}'s type must is ${validTypes.join(' or ')}.`);
if (isReturn) {
const certainType = {};
validTypes.forEach((item) => {
certainType[item] = item === type;
});
return certainType;
}
return true;
},
isExpandCell(table, columnIndex) {
return table.expandType && (
(table.showIndex && columnIndex === 1) ||
(!table.showIndex && columnIndex === 0)
);
},
isSelectionCell(table, columnIndex) {
return table.selectable && (
(table.showIndex && table.expandType && columnIndex === 2) ||
(!table.showIndex && table.expandType && columnIndex === 1) ||
(table.showIndex && !table.expandType && columnIndex === 1) ||
(!table.showIndex && !table.expandType && columnIndex === 0)
);
},
},
};

View File

@@ -1,26 +0,0 @@
let scrollBarWidth;
export default function getScrollBarWidth() {
if (typeof document === "undefined") return 0;
if (scrollBarWidth !== undefined) return scrollBarWidth;
const outer = document.createElement("div");
outer.style.visibility = "hidden";
outer.style.width = "100px";
outer.style.position = "absolute";
outer.style.top = "-9999px";
document.body.appendChild(outer);
const widthNoScroll = outer.offsetWidth;
outer.style.overflow = "scroll";
const inner = document.createElement("div");
inner.style.width = "100%";
outer.appendChild(inner);
const widthWithScroll = inner.offsetWidth;
outer.parentNode.removeChild(outer);
scrollBarWidth = widthNoScroll - widthWithScroll;
return scrollBarWidth;
}

View File

@@ -24,6 +24,7 @@ function refreshAccessToken(token) {
return service({
method: "get",
url: `/passport/user/refresh/${token}`,
_isRefreshRequest: true
});
}
@@ -40,8 +41,6 @@ function redirectToLogin(message) {
router.push("/login");
}
}
var isRefreshToken = 0;
const refreshToken = getTokenDebounce();
service.interceptors.request.use(
config => {
if (config.method == "get") {
@@ -69,113 +68,86 @@ service.interceptors.response.use(
response => {
const data = response.data;
// 根据返回的code值来做不同的处理(和后端约定)
if (!data.success && data.message) {
Message.error(data.message);
}
switch (data.code) {
case 400:
if (data.message !== null) {
Message.error(data.message);
} else {
Message.error("系统异常");
}
break;
case 20004:
case 401:
redirectToLogin(data.message !== null ? data.message : null);
case 20004:
redirectToLogin(data.message || null);
return data;
break;
case 400:
case 500:
// 系统异常
if (data.message !== null) {
Message.error(data.message);
} else {
Message.error("系统异常");
}
break;
Message.error(data.message || "系统异常");
return data;
default:
if (!data.success && data.message) {
Message.error(data.message);
}
return data;
}
},
async error => {
// 返回状态码不为200时候的错误处理
if (error.response) {
if (error.response.status === 401) {
const response = error.response;
const config = error.config;
if (response) {
const code = response.data && response.data.code;
if (response.status === 401) {
// 这种情况一般调到登录页
} else if (error.response.status === 404) {
// 避免刷新token报错
} else if (error.response.status === 403 || error.response.data.code === 20004) {
isRefreshToken++;
if (isRefreshToken === 1) {
const getTokenRes = await refreshToken();
if (getTokenRes === "success") {
// 刷新token
if (isRefreshToken === 1) {
error.response.config.headers.accessToken = getStore(
"accessToken"
);
return service(error.response.config);
} else {
const router = getRouter();
if (router) router.go(0);
}
} else {
redirectToLogin(null);
}
isRefreshToken = 0;
redirectToLogin("登录状态已过期,请重新登录");
} else if (
(response.status === 403 || code === 20004) &&
config &&
!config._isRefreshRequest &&
!config._retryAfterRefresh
) {
// accessToken 过期:刷新 token 后重试原请求
config._retryAfterRefresh = true;
const result = await refreshToken();
if (result === "success") {
config.headers.accessToken = getStore("accessToken");
return service(config);
}
redirectToLogin(null);
} else if (response.status === 404) {
// 避免刷新token报错
Message.error("请求的资源不存在");
} else {
// 其他错误处理
Message.error(error.response.data.message);
Message.error(
(response.data && response.data.message) || "系统异常,请稍后重试"
);
}
} else {
// 断网/超时等无响应的错误
Message.error(
error.code === "ECONNABORTED"
? "请求超时,请稍后重试"
: "网络异常,请检查网络连接"
);
}
/* router.push("/login") */
return Promise.resolve(error);
return Promise.reject(error);
}
);
// 防抖闭包来一波
function getTokenDebounce() {
let lock = false;
let success = false;
return function() {
if (!lock) {
lock = true;
let oldRefreshToken = getStore("refreshToken");
refreshAccessToken(oldRefreshToken)
.then(res => {
if (res.success) {
let { accessToken, refreshToken } = res.result;
setStore("accessToken", accessToken);
setStore("refreshToken", refreshToken);
success = true;
lock = false;
} else {
success = false;
lock = false;
redirectToLogin(null);
}
})
.catch(err => {
success = false;
lock = false;
});
}
return new Promise(resolve => {
// 一直看lock,直到请求失败或者成功
const timer = setInterval(() => {
if (!lock) {
clearInterval(timer);
if (success) {
resolve("success");
} else {
resolve("fail");
}
// 刷新 token 单例 Promise:并发 403 时共享同一次刷新,避免重复请求
let refreshTokenPromise = null;
function refreshToken() {
if (!refreshTokenPromise) {
const oldRefreshToken = getStore("refreshToken");
refreshTokenPromise = refreshAccessToken(oldRefreshToken)
.then(res => {
if (res.success) {
const { accessToken, refreshToken: newRefreshToken } = res.result;
setStore("accessToken", accessToken);
setStore("refreshToken", newRefreshToken);
return "success";
}
}, 500); // 轮询时间间隔
});
};
return "fail";
})
.catch(() => "fail")
.finally(() => {
refreshTokenPromise = null;
});
}
return refreshTokenPromise;
}
export const getRequest = (url, params, resBlob) => {

View File

@@ -1,6 +1,7 @@
import { createApp } from "vue";
import "core-js/stable";
import "./styles/theme.less";
import "@/styles/common.scss";
import App from "./App.vue";
import { router } from "./router/index";
import store from "./store";
@@ -85,5 +86,12 @@ router.isReady().then(() => {
app.mount("#app");
});
// axios 错误已在拦截器统一 toast 提示,页面未写 .catch 时不再触发未捕获 rejection 浮层
window.addEventListener("unhandledrejection", (event) => {
if (event.reason && event.reason.isAxiosError) {
event.preventDefault();
}
});
// 登录后由 App.vue 触发 initRouter 拉取/更新菜单
export { app, util };

View File

@@ -96,15 +96,11 @@ ul,li{
white-space: nowrap;
}
// 主题颜色
@import "./variables.scss";
$success_color: #68cabe;
$warning_color: #fa6419;
$error_color: #ff3c2a;
$theme_color: #FF5C58;
.theme_color {
color: $theme_color !important;
}
$bg_color: #f1f6fa;
.wes {
/* 多出部分用省略号表示 , 用于一行 */

View File

@@ -0,0 +1,6 @@
// 仅放变量:通过 style-resources-loader 注入到每个组件样式块,不会产生重复 CSS
$success_color: #68cabe;
$warning_color: #fa6419;
$error_color: #ff3c2a;
$theme_color: #FF5C58;
$bg_color: #f1f6fa;

View File

@@ -4,7 +4,6 @@
export function printElement(elementId, title = "打印") {
const el = document.getElementById(elementId);
if (!el) {
console.warn(`[print] element #${elementId} not found`);
return;
}
const iframe = document.createElement("iframe");

View File

@@ -142,8 +142,7 @@ export default {
value: String(item.sortOrder),
label: item.gradeName || `等级值${item.sortOrder}`,
}));
})
.catch(() => {});
});
},
formatGrade(name, gradeId) {
if (name) {

View File

@@ -60,7 +60,6 @@ export function useChat(liveId, userInfo, liveDetail) {
}
if (!groupID.value) {
initError.value = "直播间群组 ID 不存在";
console.warn("groupID 不存在,跳过 IM 初始化");
return;
}
@@ -160,18 +159,15 @@ export function useChat(liveId, userInfo, liveDetail) {
}
function onSdkNotReady() {
console.log("[SDK Not Ready]");
imReady.value = false;
}
function onSdkReady() {
console.log("[SDK Ready]");
imReady.value = true;
updateUserInfo();
}
function onKickedOut() {
console.log("[用户被踢下线]");
imReady.value = false;
initError.value = "IM 已下线,请刷新页面";
}
@@ -203,11 +199,10 @@ export function useChat(liveId, userInfo, liveDetail) {
}
try {
const imResponse = await chat.value.joinGroup({
await chat.value.joinGroup({
groupID: groupID.value,
type: TencentCloudChat.TYPES.GRP_AVCHATROOM,
});
console.log("[加群成功]", imResponse);
} catch (imError) {
console.warn("joinGroup error:", imError);
throw imError;

View File

@@ -21,7 +21,6 @@ export async function fetchLiveSetting(force = false) {
cachedSetting = res.result;
return cachedSetting;
}
console.warn("获取直播配置失败", res);
return null;
} catch (error) {
console.error("获取直播配置异常:", error);

View File

@@ -327,8 +327,7 @@ export default {
if (res.success && res.result?.buyerDomain) {
this.wapUrl = res.result.buyerDomain;
}
})
.catch(() => {});
});
},
},
};

View File

@@ -759,9 +759,7 @@ export default {
}
}
}
console.log(this.typeList)
if (this.typeList.length >= 3) {
console.log(123123)
this.getContentPrice()
}
},

View File

@@ -232,9 +232,7 @@ export default {
handleAddNav() {
this.navList.list.push({ name: "", url: "", title: "" });
},
handleMoveEnd({ newIndex, oldIndex }) {
console.log("index", newIndex, oldIndex);
},
handleMoveEnd({ newIndex, oldIndex }) {},
handleModel(type) {
if (type == "topAdvert") {
this.draftTopAdvert = JSON.parse(JSON.stringify(this.topAdvert));

View File

@@ -285,7 +285,6 @@ export default {
},
// 选择商品回调
selectedGoodsData(val) {
console.log(val);
let goods = val[0];
this.selected.img = goods.thumbnail;
this.selected.price = goods.price;

View File

@@ -160,9 +160,7 @@ export default {
watch: {
modelForm: {
deep: true,
handler: function (val) {
console.log(val);
},
handler: function (val) {},
},
},
};

View File

@@ -116,10 +116,6 @@ export default {
advertising: {
handler(val) {
this.$store.state.openStyleStore = val;
console.log(
"this.$store.state.openStyleStore",
this.$store.state.openStyleStore
);
},
deep: true,
},

View File

@@ -119,10 +119,6 @@ export default {
advertising: {
handler(val) {
this.$store.state.openStyleStore = val;
console.log(
"this.$store.state.openStyleStore",
this.$store.state.openStyleStore
);
},
deep: true,
},

View File

@@ -1,16 +1,3 @@
import * as API_Other from "@/api/other";
// 获取楼层装修信息
export function initData(id) {
API_Other.getHomeData(id).then(res => {
debugger
homeData = res;
console.log(res)
});
}
export let homeData = {};
/**
* drawer:true 广告右侧打开抽屉中显示
* drawerPromotions:true 广告右侧打开抽屉中显示

View File

@@ -621,8 +621,6 @@ export default {
return item.___index != this.goodsSelectedIndex;
});
this.res.options.list[0].titleWay[this.goodsSelectedIndex] = data;
console.log(this.res.options.list[0]);
},
// 商品排序
slotGoods(list, key, val) {
@@ -659,7 +657,6 @@ export default {
// 轮播图排序
slotBanner(val, key, index, down) {
console.log(val);
if (down == "down") {
this.downData(val.options.list, index);
} else {

View File

@@ -192,15 +192,12 @@ export default {
this.saveDialog = false;
this.$Message.error("修改失败,请稍后重试");
}
console.log(res);
})
.catch(error => {});
});
},
// 更新
update(submitWay) {
console.log(this.$store.state.openStyleStore[0], "123123123");
if (this.$store.state.openStyleStore == void 0) {
this.$Message.error("请装修楼层!");
return false;
@@ -209,7 +206,6 @@ export default {
let id = this.$route.query.id;
const pagetype = this.$route.query.pagetype;
console.log(this.$store.state.openStyleStore, submitWay);
const pageType = {
INDEX: "INDEX",
ALERT: "OPEN_SCREEN_PAGE",
@@ -239,9 +235,7 @@ export default {
this.saveDialog = false;
this.$Message.error("修改失败,请稍后重试");
}
console.log(res);
})
.catch((error) => {});
});
},
@@ -270,9 +264,7 @@ export default {
this.saveDialog = false;
this.$Message.error("保存失败,请稍后重试");
}
console.log(res);
})
.catch(error => {});
});
}
}
};

View File

@@ -16,7 +16,7 @@
>
<el-table-column prop="articleCategoryName" label="分类名称" min-width="200" />
<el-table-column prop="sort" label="排序" width="120" />
<el-table-column label="操作" width="320" align="center">
<el-table-column label="操作" width="320" align="right">
<template #default="{ row }">
<template v-if="row">
<a class="link-text" @click="edit(row)">编辑</a>

View File

@@ -17,14 +17,14 @@
title="这里展示历史某一天的热词数据统计,可根据需求配置每日持久化多少条数据。"
/>
<div id="container"></div>
<div v-show="hasData" id="container"></div>
<el-empty v-if="!hasData" description="该日期暂无热词数据" :image-size="100" />
</div>
</template>
<script>
import { Chart } from "@antv/g2";
import { getHotWordsHistory } from "@/api/index";
import { Message } from "@/utils/message";
export default {
data() {
@@ -34,6 +34,7 @@ export default {
},
hotWordsChart: null,
hotWordsData: [],
hasData: true,
endMonth: null,
};
},
@@ -60,13 +61,11 @@ export default {
const res = await getHotWordsHistory(this.params);
if (res.success) {
this.hotWordsData = res.result || [];
if (this.hotWordsChart) {
this.hasData = this.hotWordsData.length > 0;
if (this.hasData && this.hotWordsChart) {
this.hotWordsChart.changeData(this.hotWordsData);
this.hotWordsChart.render();
}
if (!this.hotWordsData.length) {
Message.error("暂无数据");
}
}
},
init() {

View File

@@ -13,11 +13,12 @@
</div>
</el-affix>
<div id="container"></div>
<el-table border :data="hotWordsData" style="width: 100%">
<div v-show="hasData" id="container"></div>
<el-table v-if="hasData" border :data="hotWordsData" style="width: 100%">
<el-table-column prop="keywords" label="热词名称" min-width="160" />
<el-table-column prop="score" label="搜索次数" min-width="120" />
</el-table>
<el-empty v-else description="暂无热词统计数据" :image-size="100" />
</div>
</template>
@@ -40,6 +41,7 @@ export default {
},
hotWordsChart: "",
hotWordsData: [],
hasData: true,
};
},
watch: {
@@ -61,7 +63,11 @@ export default {
async search() {
const res = await getHotWordsStatistics(this.params);
if (res.success) {
this.hotWordsData = res.result;
this.hotWordsData = res.result || [];
this.hasData = this.hotWordsData.length > 0;
if (!this.hasData) {
return;
}
if (!this.hotWordsChart) {
this.hotWordsChart = new Chart({
container: "container",

View File

@@ -10,13 +10,19 @@
title="这里展示今日系统中搜索前一百的搜索热词,分数为热词在排序系统中的分数,分数越高,可以在用户获取热词时进行优先展示(首页商品搜索栏下方推荐位)(分数可以填写负数,会降低推荐度)"
/>
<div class="card-list">
<el-card v-for="words in data" :key="words" class="card-item" shadow="hover">
<div class="card-item-inner">
<a href="#" class="word-link" @click.prevent="add(words)">{{ words }}</a>
<el-icon class="close-icon" @click="deleteWords(words)"><Close /></el-icon>
</div>
</el-card>
<div class="word-list">
<el-tag
v-for="words in data"
:key="words"
size="large"
closable
class="word-tag"
@close="deleteWords(words)"
@click="add(words)"
>
{{ words }}
</el-tag>
<el-empty v-if="!data.length" description="暂无热词,点击上方按钮设置" :image-size="80" />
</div>
<el-dialog
@@ -44,13 +50,11 @@
</template>
<script>
import { Close } from "@element-plus/icons-vue";
import { getHotWords, setHotWords, deleteHotWords } from "@/api/index";
import { regular } from "@/utils";
export default {
name: "todayHotWords",
components: { Close },
data() {
return {
submitLoading: false,
@@ -127,36 +131,23 @@ export default {
</script>
<style lang="scss" scoped>
.card-list {
.word-list {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
margin-top: 16px;
}
.card-item {
min-width: 100px;
margin: 10px;
}
.card-item-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.word-link {
color: var(--el-color-primary);
text-decoration: none;
}
.close-icon {
.word-tag {
cursor: pointer;
color: var(--el-text-color-secondary);
padding: 0 14px;
height: 32px;
font-size: 14px;
&:hover {
color: var(--el-color-danger);
color: var(--el-color-primary);
border-color: var(--el-color-primary);
}
}
</style>

View File

@@ -461,7 +461,6 @@ export default {
});
}
})
.catch(() => {})
.finally(() => {
this.loading = false;
});

View File

@@ -286,6 +286,8 @@ export default {
storeId: e.storeId,
storeName: e.storeName,
skuId: e.id,
goodsId: e.goodsId,
thumbnail: e.thumbnail || "",
goodsType: e.goodsType,
});
});

View File

@@ -60,29 +60,19 @@
style="width: 100%"
@sort-change="changeSort"
>
<el-table-column prop="goodsId" label="商品编号" width="180" show-overflow-tooltip />
<el-table-column label="商品图片" width="100" align="center">
<template #default="{ row }">
<img
v-if="row && row.thumbnail"
:src="row.thumbnail"
style="height: 50px; width: 50px; object-fit: cover"
/>
</template>
</el-table-column>
<el-table-column label="商品名称" min-width="200" show-overflow-tooltip>
<template #default="{ row }">
<template v-if="row">
<a class="link-text mr_10" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
<el-popover trigger="hover" title="扫码在手机中查看" placement="top" width="180">
<template #reference>
<img
src="../../../assets/qrcode.svg"
class="hover-pointer"
width="20"
height="20"
alt="qrcode"
/>
</template>
<vue-qr
:text="wapLinkTo(row.goodsId, row.skuId)"
:margin="0"
color-dark="#000"
color-light="#fff"
:size="150"
/>
</el-popover>
</template>
<a v-if="row" class="link-text" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
</template>
</el-table-column>
<el-table-column label="库存数量" width="100">
@@ -156,11 +146,9 @@
<script>
import { getKanJiaGoodsList, delKanJiaGoods } from "@/api/promotion";
import vueQr from "vue-qr";
export default {
name: "kanjia-activity-goods",
components: { vueQr },
data() {
return {
selectDate: [],
@@ -290,13 +278,6 @@ export default {
</script>
<style lang="scss" scoped>
.mr_10 {
margin-right: 10px;
}
.hover-pointer {
cursor: pointer;
vertical-align: middle;
}
.padding-row {
margin-bottom: 10px;
}

View File

@@ -69,29 +69,19 @@
class="mt_10"
style="width: 100%"
>
<el-table-column label="商品名称" min-width="200" fixed="left" show-overflow-tooltip>
<el-table-column prop="goodsId" label="商品编号" width="180" show-overflow-tooltip />
<el-table-column label="商品图片" width="100" align="center">
<template #default="{ row }">
<template v-if="row">
<a class="link-text mr_10" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
<el-popover trigger="hover" title="扫码在手机中查看" placement="top" width="180">
<template #reference>
<img
src="../../../assets/qrcode.svg"
class="hover-pointer"
width="20"
height="20"
alt="qrcode"
/>
</template>
<vue-qr
:text="wapLinkTo(row.goodsId, row.skuId)"
:margin="0"
color-dark="#000"
color-light="#fff"
:size="150"
/>
</el-popover>
</template>
<img
v-if="row && row.thumbnail"
:src="row.thumbnail"
style="height: 50px; width: 50px; object-fit: cover"
/>
</template>
</el-table-column>
<el-table-column label="商品名称" min-width="200" show-overflow-tooltip>
<template #default="{ row }">
<a v-if="row" class="link-text" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
</template>
</el-table-column>
<el-table-column label="市场价" width="110">
@@ -171,11 +161,9 @@ import {
editPointsGoodsStatus,
deletePointsGoodsStatus,
} from "@/api/promotion";
import vueQr from "vue-qr";
export default {
name: "pointsGoods",
components: { vueQr },
data() {
return {
loading: true,
@@ -307,13 +295,6 @@ export default {
cursor: pointer;
text-decoration: none;
}
.mr_10 {
margin-right: 10px;
}
.hover-pointer {
cursor: pointer;
vertical-align: middle;
}
.op-split {
margin: 0 8px;
color: #dcdee2;

View File

@@ -43,38 +43,38 @@
<div>
<h3 class="ml_10">结算详细</h3>
<el-table :data="billData" border style="width: 100%">
<el-table-column label="结算金额" prop="billPrice" align="center" width="200" fixed="left">
<el-table-column label="结算金额" prop="billPrice" align="center" width="160" fixed="left">
<template #default="{ row }">
<span v-if="row">{{ formatPrice(row.billPrice) }}</span>
</template>
</el-table-column>
<el-table-column label="收入" align="center">
<el-table-column label="用户实付" prop="orderPrice" align="center" width="200">
<el-table-column label="用户实付" prop="orderPrice" align="center" width="160">
<template #default="{ row }">
<span v-if="row">{{ formatPrice(row.orderPrice) }}</span>
</template>
</el-table-column>
<el-table-column label="平台优惠券补贴" prop="siteCouponCommission" align="center" width="200">
<el-table-column label="平台优惠券补贴" prop="siteCouponCommission" align="center" width="160">
<template #default="{ row }">
<span v-if="row">{{ formatPrice(row.siteCouponCommission) }}</span>
</template>
</el-table-column>
<el-table-column label="积分商品补贴" prop="pointSettlementPrice" align="center" width="200">
<el-table-column label="积分商品补贴" prop="pointSettlementPrice" align="center" width="160">
<template #default="{ row }">
<span v-if="row">{{ formatPrice(row.pointSettlementPrice) }}</span>
</template>
</el-table-column>
<el-table-column label="砍价商品补贴" prop="kanjiaSettlementPrice" align="center" width="200">
<el-table-column label="砍价商品补贴" prop="kanjiaSettlementPrice" align="center" width="160">
<template #default="{ row }">
<span v-if="row">{{ formatPrice(row.kanjiaSettlementPrice) }}</span>
</template>
</el-table-column>
<el-table-column label="礼品卡补贴" prop="giftCardSubsidy" align="center" width="200">
<el-table-column label="礼品卡补贴" prop="giftCardSubsidy" align="center" width="160">
<template #default="{ row }">
<span v-if="row">{{ formatPrice(row.giftCardSubsidy) }}</span>
</template>
</el-table-column>
<el-table-column label="分销佣金" prop="distributionCommission" align="center" width="200">
<el-table-column label="分销佣金" prop="distributionCommission" align="center" width="160">
<template #default="{ row }">
<priceColorScheme
v-if="row"
@@ -83,7 +83,7 @@
/>
</template>
</el-table-column>
<el-table-column label="平台服务费" prop="commissionPrice" align="center" width="200">
<el-table-column label="平台服务费" prop="commissionPrice" align="center" width="160">
<template #default="{ row }">
<priceColorScheme
v-if="row"
@@ -94,7 +94,7 @@
</el-table-column>
</el-table-column>
<el-table-column label="退款" align="center">
<el-table-column label="退款金额" prop="refundPrice" align="center" width="200">
<el-table-column label="退款金额" prop="refundPrice" align="center" width="160">
<template #default="{ row }">
<priceColorScheme v-if="row" :value="negate(row.refundPrice)" :color="$mainColor" />
</template>
@@ -103,7 +103,7 @@
label="退单返还平台优惠券补贴"
prop="siteCouponRefundCommission"
align="center"
width="200"
width="160"
>
<template #default="{ row }">
<priceColorScheme
@@ -117,7 +117,7 @@
label="退单返还积分商品补贴"
prop="pointRefundSettlementPrice"
align="center"
width="200"
width="160"
>
<template #default="{ row }">
<priceColorScheme
@@ -131,7 +131,7 @@
label="退单返还砍价商品补贴"
prop="kanjiaRefundSettlementPrice"
align="center"
width="200"
width="160"
>
<template #default="{ row }">
<priceColorScheme
@@ -141,7 +141,7 @@
/>
</template>
</el-table-column>
<el-table-column label="礼品卡返还" prop="giftCardRefundSubsidy" align="center" width="200">
<el-table-column label="礼品卡返还" prop="giftCardRefundSubsidy" align="center" width="160">
<template #default="{ row }">
<priceColorScheme
v-if="row"
@@ -154,7 +154,7 @@
label="退单返还分销佣金"
prop="distributionRefundCommission"
align="center"
width="200"
width="160"
>
<template #default="{ row }">
<span v-if="row">{{ formatPrice(row.distributionRefundCommission) }}</span>
@@ -164,7 +164,7 @@
label="退单返还平台服务费"
prop="refundCommissionPrice"
align="center"
width="200"
width="160"
>
<template #default="{ row }">
<span v-if="row">{{ formatPrice(row.refundCommissionPrice) }}</span>
@@ -450,9 +450,6 @@ export default {
} else {
navigator.msSaveBlob(blob, "结算单-" + this.id + ".xlsx");
}
})
.catch((err) => {
console.log(err);
});
},
init() {
@@ -556,12 +553,16 @@ table {
margin-left: 40px;
tr {
font-size: 12px;
height: 40px;
padding: 10px;
td:nth-child(1) {
width: 120px;
width: 140px;
color: #909399;
}
td:nth-child(2) {
color: #303133;
font-weight: 500;
}
}
}

View File

@@ -1,6 +1,56 @@
<template>
<div class="search">
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<el-form-item label="账单号">
<el-input
v-model="searchForm.sn"
placeholder="请输入账单号"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item label="店铺名称">
<el-input
v-model="searchForm.storeName"
placeholder="请输入店铺名称"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item label="生成时间">
<el-date-picker
v-model="selectDate"
type="daterange"
clearable
value-format="yyyy-MM-dd HH:mm:ss"
:default-time="defaultTime"
start-placeholder="开始日期"
end-placeholder="结束日期"
style="width: 260px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<el-tabs v-model="activeStatus" @tab-change="handleStatusChange">
<el-tab-pane label="全部" name="" />
<el-tab-pane label="已出账" name="OUT" />
<el-tab-pane label="已对账" name="CHECK" />
<el-tab-pane label="已付款" name="COMPLETE" />
</el-tabs>
<div class="mb_10">
<el-button type="primary" @click="handleExportList">导出列表</el-button>
<el-button :disabled="selectCount === 0" @click="handleBatchDownload">批量下载</el-button>
@@ -66,11 +116,19 @@ export default {
data() {
return {
loading: true,
selectDate: [],
defaultTime: [new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)],
activeStatus: "",
searchForm: {
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
sn: "",
storeName: "",
billStatus: "",
startDate: null,
endDate: null,
},
selectList: [],
selectCount: 0,
@@ -102,14 +160,32 @@ export default {
this.selectList = e;
this.selectCount = e.length;
},
handleStatusChange(name) {
this.searchForm.billStatus = name;
this.searchForm.pageNumber = 1;
this.getDataList();
},
handleSearch() {
this.searchForm.pageNumber = 1;
if (this.selectDate && this.selectDate[0] && this.selectDate[1]) {
this.searchForm.startDate = this.selectDate[0];
this.searchForm.endDate = this.selectDate[1];
} else {
this.searchForm.startDate = null;
this.searchForm.endDate = null;
}
this.getDataList();
},
handleReset() {
this.selectDate = [];
this.searchForm.sn = "";
this.searchForm.storeName = "";
this.searchForm.startDate = null;
this.searchForm.endDate = null;
this.handleSearch();
},
getDataList() {
this.loading = true;
if (this.searchForm.startTime) {
this.searchForm.startTime = this.$filters.unixToDate(this.searchForm.startTime / 1000);
}
if (this.searchForm.endTime) {
this.searchForm.endTime = this.$filters.unixToDate(this.searchForm.endTime / 1000);
}
API_Shop.getBuyBillPage(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {

View File

@@ -282,7 +282,7 @@
</el-tab-pane>
<el-tab-pane label="结算信息" class="tab" name="settlement">
<el-alert type="error" :closable="false" show-icon>
<el-alert type="info" :closable="false" show-icon>
已添加<span class="theme_color">{{ settlementCycle.length }}</span
>个结算日,最多可添加5个结算日,当月不包含所设日期时,将会顺延到下一个结算日
</el-alert>

View File

@@ -27,6 +27,7 @@
</template>
<script>
import { markRaw } from "vue";
import {
ArrowRight,
DataAnalysis,
@@ -40,25 +41,25 @@ const REPORT_MODULES = [
name: "storePerformanceReport",
title: "店铺业绩报表",
desc: "支付金额、营业额、转化率及同环比分析",
icon: TrendCharts,
icon: markRaw(TrendCharts),
},
{
name: "goodsComparisonReport",
title: "商品同比环比报表",
desc: "商品销售金额、销量占比及同环比对比",
icon: DataAnalysis,
icon: markRaw(DataAnalysis),
},
{
name: "salesOrderDetailReport",
title: "销售订单明细报表",
desc: "订单流水、收款明细与优惠明细",
icon: Document,
icon: markRaw(Document),
},
{
name: "goodsSalesSummaryReport",
title: "商品销售汇总报表",
desc: "商品销量、净销售额及优惠汇总",
icon: Goods,
icon: markRaw(Goods),
},
];

View File

@@ -175,9 +175,11 @@ export default {
this.loadData();
},
deep: true,
immediate: true,
},
},
mounted() {
this.loadData();
},
beforeUnmount() {
if (this.trendChart) {
this.trendChart.destroy();
@@ -244,6 +246,8 @@ export default {
this.trendChart = null;
}
this.$nextTick(() => {
const container = document.getElementById("memberTrendChart");
if (!container) return;
this.trendChart = new Chart({
container: "memberTrendChart",
autoFit: true,
@@ -280,13 +284,15 @@ export default {
},
async loadData() {
const query = { ...this.params };
const [overviewRes, analysisRes, genderRes, regionRes, trendRes] = await Promise.all([
API_Statistics.getMemberOverview(query),
API_Statistics.getMemberAnalysis(query),
API_Statistics.getMemberGenderDistribution(),
API_Statistics.getMemberRegionDistribution(),
API_Statistics.getMemberNewTrend(query),
]);
const [overviewRes, analysisRes, genderRes, regionRes, trendRes] = (
await Promise.allSettled([
API_Statistics.getMemberOverview(query),
API_Statistics.getMemberAnalysis(query),
API_Statistics.getMemberGenderDistribution(),
API_Statistics.getMemberRegionDistribution(),
API_Statistics.getMemberNewTrend(query),
])
).map((r) => (r.status === "fulfilled" ? r.value : null));
if (overviewRes && overviewRes.success) {
this.overview = overviewRes.result || {};
}

View File

@@ -458,7 +458,6 @@ export default {
clickBreadcrumb(item, index) {
let callback = JSON.parse(JSON.stringify(item));
console.log("callback",callback)
this.orderParams = callback;
this.overViewParams = callback;
@@ -482,6 +481,8 @@ export default {
this.chartList = res.result;
if (!this.orderChart) {
const container = document.getElementById("orderChart");
if (!container) return;
this.orderChart = new Chart({
container: "orderChart",
autoFit: true,

View File

@@ -1103,7 +1103,6 @@ export default {
handleContextMenu(val) {
console.log('handleContextMenu', val);
this.selectedGroupData = val;
},
// 编辑分组
@@ -1450,10 +1449,7 @@ export default {
this.init();
ElMessage.success("批量删除文件成功");
}
})
.catch((err) => {
console.log("失败", err);
});
});
}).catch(() => {});
},
// 单个删除文件

View File

@@ -70,7 +70,6 @@ export default {
this.selected = v;
getSetting(v).then((res) => {
if (res.result) {
console.log(res);
this.modalVisible = true;
this.form = res;
}

View File

@@ -36,6 +36,16 @@ module.exports = {
if (message.includes("ResizeObserver loop")) {
return false;
}
// axios 请求错误已由拦截器统一 toast 提示,不再重复弹浮层
if (
error?.isAxiosError ||
error?.name === "AxiosError" ||
message.includes("Request failed with status code") ||
message.includes("Network Error") ||
message.includes("timeout of")
) {
return false;
}
return true;
},
},
@@ -78,11 +88,19 @@ module.exports = {
args[0].cdn = { css: [], js: [] };
return args;
});
if (enableProduction) {
// 生产环境移除所有 console.* 调用
config.optimization.minimizer("terser").tap((args) => {
const terserOptions = args[0].terserOptions || (args[0].terserOptions = {});
terserOptions.compress = { ...terserOptions.compress, drop_console: true };
return args;
});
}
},
pluginOptions: {
"style-resources-loader": {
preProcessor: "scss",
patterns: [path.resolve(__dirname, "./src/styles/common.scss")],
patterns: [path.resolve(__dirname, "./src/styles/variables.scss")],
},
},
};