1.新增批量导入 2.新增财务报表
This commit is contained in:
@@ -10,6 +10,7 @@ VITE_APP_BASE_API=/dev-api
|
|||||||
# VITE_APP_API_URL=https://cashier.sxczgkj.com/ # 正式
|
# VITE_APP_API_URL=https://cashier.sxczgkj.com/ # 正式
|
||||||
VITE_APP_API_URL=http://192.168.1.42/ # 本地
|
VITE_APP_API_URL=http://192.168.1.42/ # 本地
|
||||||
VITE_APP_API_PHP_URL=http://192.168.1.42:8000 #php抖音美团测试环境
|
VITE_APP_API_PHP_URL=http://192.168.1.42:8000 #php抖音美团测试环境
|
||||||
|
VITE_APP_API_PHP_IMPORT_URL=http://192.168.1.42:8789 #本地php批量导入
|
||||||
|
|
||||||
|
|
||||||
# WebSocket 端点(不配置则关闭),线上 ws://api.youlai.tech/ws ,本地 ws://localhost:8989/ws
|
# WebSocket 端点(不配置则关闭),线上 ws://api.youlai.tech/ws ,本地 ws://localhost:8989/ws
|
||||||
|
|||||||
@@ -72,6 +72,23 @@ const OrderApi = {
|
|||||||
data
|
data
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// 查询财务报表
|
||||||
|
financeSts(params: any) {
|
||||||
|
return request<any>({
|
||||||
|
url: `${Order_BaseUrl}/admin/finance/sts`,
|
||||||
|
method: "get",
|
||||||
|
params
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 导出财务报表
|
||||||
|
financeExport(params: any) {
|
||||||
|
return request<any>({
|
||||||
|
url: `${Order_BaseUrl}/admin/finance/export`,
|
||||||
|
method: "get",
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OrderApi;
|
export default OrderApi;
|
||||||
|
|||||||
@@ -3,68 +3,49 @@
|
|||||||
<!-- 表格工具栏 -->
|
<!-- 表格工具栏 -->
|
||||||
<div class="flex-x-between mb-[10px]">
|
<div class="flex-x-between mb-[10px]">
|
||||||
<!-- 左侧工具栏 -->
|
<!-- 左侧工具栏 -->
|
||||||
<div>
|
<div style="display: flex;">
|
||||||
<template v-for="item in toolbar" :key="item">
|
<template v-for="item in toolbar" :key="item">
|
||||||
<template v-if="typeof item === 'string'">
|
<template v-if="typeof item === 'string'">
|
||||||
<!-- 新增 -->
|
<!-- 新增 -->
|
||||||
<template v-if="item === 'add'">
|
<template v-if="item === 'add'">
|
||||||
<el-button
|
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="primary" icon="plus"
|
||||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
@click="handleToolbar(item)">
|
||||||
type="primary"
|
|
||||||
icon="plus"
|
|
||||||
@click="handleToolbar(item)"
|
|
||||||
>
|
|
||||||
新增
|
新增
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<!-- 删除 -->
|
<!-- 删除 -->
|
||||||
<template v-else-if="item === 'delete'">
|
<template v-else-if="item === 'delete'">
|
||||||
<el-button
|
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="danger" icon="delete"
|
||||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
:disabled="removeIds.length === 0" @click="handleToolbar(item)">
|
||||||
type="danger"
|
|
||||||
icon="delete"
|
|
||||||
:disabled="removeIds.length === 0"
|
|
||||||
@click="handleToolbar(item)"
|
|
||||||
>
|
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<!-- 导入 -->
|
<!-- 导入 -->
|
||||||
<template v-else-if="item === 'import'">
|
<template v-else-if="item === 'import'">
|
||||||
<el-button
|
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="default" icon="upload"
|
||||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
@click="handleToolbar(item)">
|
||||||
type="default"
|
|
||||||
icon="upload"
|
|
||||||
@click="handleToolbar(item)"
|
|
||||||
>
|
|
||||||
导入
|
导入
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<!-- 导出 -->
|
<!-- 导出 -->
|
||||||
<template v-else-if="item === 'export'">
|
<template v-else-if="item === 'export'">
|
||||||
<el-button
|
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="default" icon="download"
|
||||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
@click="handleToolbar(item)">
|
||||||
type="default"
|
|
||||||
icon="download"
|
|
||||||
@click="handleToolbar(item)"
|
|
||||||
>
|
|
||||||
导出
|
导出
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<!-- 其他 -->
|
<!-- 其他 -->
|
||||||
<template v-else-if="typeof item === 'object'">
|
<template v-else-if="typeof item === 'object'">
|
||||||
<el-button
|
<el-button v-if="item.hidden === undefined || item.hidden === false"
|
||||||
v-if="item.hidden === undefined || item.hidden === false"
|
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]" :icon="item.icon" :type="item.type ?? 'default'"
|
||||||
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
@click="handleToolbar(item.name)">
|
||||||
:icon="item.icon"
|
|
||||||
:type="item.type ?? 'default'"
|
|
||||||
@click="handleToolbar(item.name)"
|
|
||||||
>
|
|
||||||
{{ item.text }}
|
{{ item.text }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- 插槽 -->
|
||||||
|
<slot name="custom" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 右侧工具栏 -->
|
<!-- 右侧工具栏 -->
|
||||||
<div>
|
<div>
|
||||||
@@ -89,68 +70,36 @@
|
|||||||
</template>
|
</template>
|
||||||
<!-- 导出 -->
|
<!-- 导出 -->
|
||||||
<template v-else-if="item === 'exports'">
|
<template v-else-if="item === 'exports'">
|
||||||
<el-button
|
<el-button v-hasPerm="[`${contentConfig.pageName}:export`]" icon="download" circle title="导出"
|
||||||
v-hasPerm="[`${contentConfig.pageName}:export`]"
|
@click="handleToolbar(item)" />
|
||||||
icon="download"
|
|
||||||
circle
|
|
||||||
title="导出"
|
|
||||||
@click="handleToolbar(item)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<!-- 导入 -->
|
<!-- 导入 -->
|
||||||
<template v-else-if="item === 'imports'">
|
<template v-else-if="item === 'imports'">
|
||||||
<el-button
|
<el-button v-hasPerm="[`${contentConfig.pageName}:import`]" icon="upload" circle title="导入"
|
||||||
v-hasPerm="[`${contentConfig.pageName}:import`]"
|
@click="handleToolbar(item)" />
|
||||||
icon="upload"
|
|
||||||
circle
|
|
||||||
title="导入"
|
|
||||||
@click="handleToolbar(item)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<!-- 搜索 -->
|
<!-- 搜索 -->
|
||||||
<template v-else-if="item === 'search'">
|
<template v-else-if="item === 'search'">
|
||||||
<el-button
|
<el-button v-hasPerm="[`${contentConfig.pageName}:query`]" icon="search" circle title="搜索"
|
||||||
v-hasPerm="[`${contentConfig.pageName}:query`]"
|
@click="handleToolbar(item)" />
|
||||||
icon="search"
|
|
||||||
circle
|
|
||||||
title="搜索"
|
|
||||||
@click="handleToolbar(item)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<!-- 其他 -->
|
<!-- 其他 -->
|
||||||
<template v-else-if="typeof item === 'object'">
|
<template v-else-if="typeof item === 'object'">
|
||||||
<template v-if="item.auth">
|
<template v-if="item.auth">
|
||||||
<el-button
|
<el-button v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]" :icon="item.icon" circle
|
||||||
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
:title="item.title" @click="handleToolbar(item.name)" />
|
||||||
:icon="item.icon"
|
|
||||||
circle
|
|
||||||
:title="item.title"
|
|
||||||
@click="handleToolbar(item.name)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-button
|
<el-button :icon="item.icon" circle :title="item.title" @click="handleToolbar(item.name)" />
|
||||||
:icon="item.icon"
|
|
||||||
circle
|
|
||||||
:title="item.title"
|
|
||||||
@click="handleToolbar(item.name)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<el-table
|
<el-table ref="tableRef" v-loading="loading" v-bind="contentConfig.table" :data="pageData" :row-key="pk"
|
||||||
ref="tableRef"
|
@selection-change="handleSelectionChange" @filter-change="handleFilterChange">
|
||||||
v-loading="loading"
|
|
||||||
v-bind="contentConfig.table"
|
|
||||||
:data="pageData"
|
|
||||||
:row-key="pk"
|
|
||||||
@selection-change="handleSelectionChange"
|
|
||||||
@filter-change="handleFilterChange"
|
|
||||||
>
|
|
||||||
<template v-for="col in cols" :key="col">
|
<template v-for="col in cols" :key="col">
|
||||||
<el-table-column v-if="col.show" v-bind="col">
|
<el-table-column v-if="col.show" v-bind="col">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@@ -159,24 +108,15 @@
|
|||||||
<template v-if="col.prop">
|
<template v-if="col.prop">
|
||||||
<template v-if="Array.isArray(scope.row[col.prop])">
|
<template v-if="Array.isArray(scope.row[col.prop])">
|
||||||
<template v-for="(item, index) in scope.row[col.prop]" :key="item">
|
<template v-for="(item, index) in scope.row[col.prop]" :key="item">
|
||||||
<el-image
|
<el-image :src="item" :preview-src-list="scope.row[col.prop]" :initial-index="index"
|
||||||
:src="item"
|
:preview-teleported="true" :style="`width: ${col.imageWidth ?? 40}px; height: ${col.imageHeight ?? 40
|
||||||
:preview-src-list="scope.row[col.prop]"
|
}px`" />
|
||||||
:initial-index="index"
|
|
||||||
:preview-teleported="true"
|
|
||||||
:style="`width: ${col.imageWidth ?? 40}px; height: ${
|
|
||||||
col.imageHeight ?? 40
|
|
||||||
}px`"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-image
|
<el-image :src="scope.row[col.prop]" :preview-src-list="[scope.row[col.prop]]"
|
||||||
:src="scope.row[col.prop]"
|
|
||||||
:preview-src-list="[scope.row[col.prop]]"
|
|
||||||
:preview-teleported="true"
|
:preview-teleported="true"
|
||||||
:style="`width: ${col.imageWidth ?? 40}px; height: ${col.imageHeight ?? 40}px`"
|
:style="`width: ${col.imageWidth ?? 40}px; height: ${col.imageHeight ?? 40}px`" />
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
@@ -198,30 +138,20 @@
|
|||||||
<template v-else-if="col.templet === 'switch'">
|
<template v-else-if="col.templet === 'switch'">
|
||||||
<template v-if="col.prop">
|
<template v-if="col.prop">
|
||||||
<!-- pageData.length>0: 解决el-switch组件会在表格初始化的时候触发一次change事件 -->
|
<!-- pageData.length>0: 解决el-switch组件会在表格初始化的时候触发一次change事件 -->
|
||||||
<el-switch
|
<el-switch v-model="scope.row[col.prop]" :active-value="col.activeValue ?? 1"
|
||||||
v-model="scope.row[col.prop]"
|
:inactive-value="col.inactiveValue ?? 0" :inline-prompt="true" :active-text="col.activeText ?? ''"
|
||||||
:active-value="col.activeValue ?? 1"
|
:inactive-text="col.inactiveText ?? ''" :validate-event="false"
|
||||||
:inactive-value="col.inactiveValue ?? 0"
|
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)" @change="
|
||||||
:inline-prompt="true"
|
|
||||||
:active-text="col.activeText ?? ''"
|
|
||||||
:inactive-text="col.inactiveText ?? ''"
|
|
||||||
:validate-event="false"
|
|
||||||
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)"
|
|
||||||
@change="
|
|
||||||
pageData.length > 0 && handleModify(col.prop, scope.row[col.prop], scope.row)
|
pageData.length > 0 && handleModify(col.prop, scope.row[col.prop], scope.row)
|
||||||
"
|
" />
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<!-- 生成输入框组件 -->
|
<!-- 生成输入框组件 -->
|
||||||
<template v-else-if="col.templet === 'input'">
|
<template v-else-if="col.templet === 'input'">
|
||||||
<template v-if="col.prop">
|
<template v-if="col.prop">
|
||||||
<el-input
|
<el-input v-model="scope.row[col.prop]" :type="col.inputType ?? 'text'"
|
||||||
v-model="scope.row[col.prop]"
|
|
||||||
:type="col.inputType ?? 'text'"
|
|
||||||
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)"
|
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)"
|
||||||
@blur="handleModify(col.prop, scope.row[col.prop], scope.row)"
|
@blur="handleModify(col.prop, scope.row[col.prop], scope.row)" />
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<!-- 格式化为价格 -->
|
<!-- 格式化为价格 -->
|
||||||
@@ -253,7 +183,7 @@
|
|||||||
{{
|
{{
|
||||||
scope.row[col.prop]
|
scope.row[col.prop]
|
||||||
? useDateFormat(scope.row[col.prop], col.dateFormat ?? "YYYY-MM-DD HH:mm:ss")
|
? useDateFormat(scope.row[col.prop], col.dateFormat ?? "YYYY-MM-DD HH:mm:ss")
|
||||||
.value
|
.value
|
||||||
: ""
|
: ""
|
||||||
}}
|
}}
|
||||||
</template>
|
</template>
|
||||||
@@ -264,21 +194,15 @@
|
|||||||
<template v-if="typeof item === 'string'">
|
<template v-if="typeof item === 'string'">
|
||||||
<!-- 编辑/删除 -->
|
<!-- 编辑/删除 -->
|
||||||
<template v-if="item === 'edit' || item === 'delete'">
|
<template v-if="item === 'edit' || item === 'delete'">
|
||||||
<el-button
|
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
:type="item === 'edit' ? 'primary' : 'danger'" :icon="item" size="small" link @click="
|
||||||
:type="item === 'edit' ? 'primary' : 'danger'"
|
|
||||||
:icon="item"
|
|
||||||
size="small"
|
|
||||||
link
|
|
||||||
@click="
|
|
||||||
handleOperat({
|
handleOperat({
|
||||||
name: item,
|
name: item,
|
||||||
row: scope.row,
|
row: scope.row,
|
||||||
column: scope.column,
|
column: scope.column,
|
||||||
$index: scope.$index,
|
$index: scope.$index,
|
||||||
})
|
})
|
||||||
"
|
">
|
||||||
>
|
|
||||||
{{ item === "edit" ? "编辑" : "删除" }}
|
{{ item === "edit" ? "编辑" : "删除" }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -286,63 +210,43 @@
|
|||||||
<!-- 其他 -->
|
<!-- 其他 -->
|
||||||
<template v-else-if="typeof item === 'object'">
|
<template v-else-if="typeof item === 'object'">
|
||||||
<template v-if="item.hidden === undefined || item.hidden === false">
|
<template v-if="item.hidden === undefined || item.hidden === false">
|
||||||
<el-button
|
<el-button v-if="item.isBtn" v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
||||||
v-if="item.isBtn"
|
:icon="item.icon" :type="item.type ?? 'primary'" size="small" link @click="
|
||||||
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
|
||||||
:icon="item.icon"
|
|
||||||
:type="item.type ?? 'primary'"
|
|
||||||
size="small"
|
|
||||||
link
|
|
||||||
@click="
|
|
||||||
handleOperat({
|
handleOperat({
|
||||||
name: item.name,
|
name: item.name,
|
||||||
row: scope.row,
|
row: scope.row,
|
||||||
column: scope.column,
|
column: scope.column,
|
||||||
$index: scope.$index,
|
$index: scope.$index,
|
||||||
})
|
})
|
||||||
"
|
">
|
||||||
>
|
|
||||||
{{ item.text }}
|
{{ item.text }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-dropdown style="margin-top: 4px" v-else>
|
<el-dropdown style="margin-top: 4px" v-else>
|
||||||
<el-button
|
<el-button v-if="item.render === undefined || item.render(scope.row)" v-bind="item.auth
|
||||||
v-if="item.render === undefined || item.render(scope.row)"
|
? { 'v-hasPerm': [`${contentConfig.pageName}:${item.auth}`] }
|
||||||
v-bind="
|
: {}
|
||||||
item.auth
|
" :icon="item.icon" :type="item.type ?? 'primary'" size="small" link @click="
|
||||||
? { 'v-hasPerm': [`${contentConfig.pageName}:${item.auth}`] }
|
|
||||||
: {}
|
|
||||||
"
|
|
||||||
:icon="item.icon"
|
|
||||||
:type="item.type ?? 'primary'"
|
|
||||||
size="small"
|
|
||||||
link
|
|
||||||
@click="
|
|
||||||
handleOperat({
|
handleOperat({
|
||||||
name: item.name,
|
name: item.name,
|
||||||
row: scope.row,
|
row: scope.row,
|
||||||
column: scope.column,
|
column: scope.column,
|
||||||
$index: scope.$index,
|
$index: scope.$index,
|
||||||
})
|
})
|
||||||
"
|
">
|
||||||
>
|
|
||||||
{{ item.text }}
|
{{ item.text }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<template #dropdown v-if="item.options && item.options.length > 0">
|
<template #dropdown v-if="item.options && item.options.length > 0">
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item
|
<el-dropdown-item @click="
|
||||||
@click="
|
handleOperat({
|
||||||
handleOperat({
|
name: item.name,
|
||||||
name: item.name,
|
row: scope.row,
|
||||||
row: scope.row,
|
column: scope.column,
|
||||||
column: scope.column,
|
$index: scope.$index,
|
||||||
$index: scope.$index,
|
command: opt.command ? opt.command : '',
|
||||||
command: opt.command ? opt.command : '',
|
})
|
||||||
})
|
" v-for="opt in item.options" :key="opt.value">
|
||||||
"
|
|
||||||
v-for="opt in item.options"
|
|
||||||
:key="opt.value"
|
|
||||||
>
|
|
||||||
{{ opt.label }}
|
{{ opt.label }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
@@ -364,33 +268,18 @@
|
|||||||
<template v-if="showPagination">
|
<template v-if="showPagination">
|
||||||
<el-scrollbar>
|
<el-scrollbar>
|
||||||
<div class="mt-[12px]">
|
<div class="mt-[12px]">
|
||||||
<el-pagination
|
<el-pagination v-bind="pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||||
v-bind="pagination"
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</template>
|
</template>
|
||||||
<!-- 导出弹窗 -->
|
<!-- 导出弹窗 -->
|
||||||
<el-dialog
|
<el-dialog v-model="exportsModalVisible" :align-center="true" title="导出数据" width="600px" style="padding-right: 0"
|
||||||
v-model="exportsModalVisible"
|
@close="handleCloseExportsModal">
|
||||||
:align-center="true"
|
|
||||||
title="导出数据"
|
|
||||||
width="600px"
|
|
||||||
style="padding-right: 0"
|
|
||||||
@close="handleCloseExportsModal"
|
|
||||||
>
|
|
||||||
<!-- 滚动 -->
|
<!-- 滚动 -->
|
||||||
<el-scrollbar max-height="60vh">
|
<el-scrollbar max-height="60vh">
|
||||||
<!-- 表单 -->
|
<!-- 表单 -->
|
||||||
<el-form
|
<el-form ref="exportsFormRef" label-width="auto" style="padding-right: var(--el-dialog-padding-primary)"
|
||||||
ref="exportsFormRef"
|
:model="exportsFormData" :rules="exportsFormRules">
|
||||||
label-width="auto"
|
|
||||||
style="padding-right: var(--el-dialog-padding-primary)"
|
|
||||||
:model="exportsFormData"
|
|
||||||
:rules="exportsFormRules"
|
|
||||||
>
|
|
||||||
<el-form-item label="文件名" prop="filename">
|
<el-form-item label="文件名" prop="filename">
|
||||||
<el-input v-model="exportsFormData.filename" clearable />
|
<el-input v-model="exportsFormData.filename" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -400,16 +289,10 @@
|
|||||||
<el-form-item label="数据源" prop="origin">
|
<el-form-item label="数据源" prop="origin">
|
||||||
<el-select v-model="exportsFormData.origin">
|
<el-select v-model="exportsFormData.origin">
|
||||||
<el-option label="当前数据 (当前页的数据)" :value="ExportsOriginEnum.CURRENT" />
|
<el-option label="当前数据 (当前页的数据)" :value="ExportsOriginEnum.CURRENT" />
|
||||||
<el-option
|
<el-option label="选中数据 (所有选中的数据)" :value="ExportsOriginEnum.SELECTED"
|
||||||
label="选中数据 (所有选中的数据)"
|
:disabled="selectionData.length <= 0" />
|
||||||
:value="ExportsOriginEnum.SELECTED"
|
<el-option label="全量数据 (所有分页的数据)" :value="ExportsOriginEnum.REMOTE"
|
||||||
:disabled="selectionData.length <= 0"
|
:disabled="contentConfig.exportsAction === undefined" />
|
||||||
/>
|
|
||||||
<el-option
|
|
||||||
label="全量数据 (所有分页的数据)"
|
|
||||||
:value="ExportsOriginEnum.REMOTE"
|
|
||||||
:disabled="contentConfig.exportsAction === undefined"
|
|
||||||
/>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="字段" prop="fields">
|
<el-form-item label="字段" prop="fields">
|
||||||
@@ -430,35 +313,17 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<!-- 导入弹窗 -->
|
<!-- 导入弹窗 -->
|
||||||
<el-dialog
|
<el-dialog v-model="importModalVisible" :align-center="true" title="导入数据" width="600px" style="padding-right: 0"
|
||||||
v-model="importModalVisible"
|
@close="handleCloseImportModal">
|
||||||
:align-center="true"
|
|
||||||
title="导入数据"
|
|
||||||
width="600px"
|
|
||||||
style="padding-right: 0"
|
|
||||||
@close="handleCloseImportModal"
|
|
||||||
>
|
|
||||||
<!-- 滚动 -->
|
<!-- 滚动 -->
|
||||||
<el-scrollbar max-height="60vh">
|
<el-scrollbar max-height="60vh">
|
||||||
<!-- 表单 -->
|
<!-- 表单 -->
|
||||||
<el-form
|
<el-form ref="importFormRef" label-width="auto" style="padding-right: var(--el-dialog-padding-primary)"
|
||||||
ref="importFormRef"
|
:model="importFormData" :rules="importFormRules">
|
||||||
label-width="auto"
|
|
||||||
style="padding-right: var(--el-dialog-padding-primary)"
|
|
||||||
:model="importFormData"
|
|
||||||
:rules="importFormRules"
|
|
||||||
>
|
|
||||||
<el-form-item label="文件名" prop="files">
|
<el-form-item label="文件名" prop="files">
|
||||||
<el-upload
|
<el-upload ref="uploadRef" v-model:file-list="importFormData.files" class="w-full"
|
||||||
ref="uploadRef"
|
|
||||||
v-model:file-list="importFormData.files"
|
|
||||||
class="w-full"
|
|
||||||
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
||||||
:drag="true"
|
:drag="true" :limit="1" :auto-upload="false" :on-exceed="handleFileExceed">
|
||||||
:limit="1"
|
|
||||||
:auto-upload="false"
|
|
||||||
:on-exceed="handleFileExceed"
|
|
||||||
>
|
|
||||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||||
<div class="el-upload__text">
|
<div class="el-upload__text">
|
||||||
<span>将文件拖到此处,或</span>
|
<span>将文件拖到此处,或</span>
|
||||||
@@ -467,13 +332,8 @@
|
|||||||
<template #tip>
|
<template #tip>
|
||||||
<div class="el-upload__tip">
|
<div class="el-upload__tip">
|
||||||
*.xlsx / *.xls
|
*.xlsx / *.xls
|
||||||
<el-link
|
<el-link v-if="contentConfig.importTemplate" type="primary" icon="download" :underline="false"
|
||||||
v-if="contentConfig.importTemplate"
|
@click="handleDownloadTemplate">
|
||||||
type="primary"
|
|
||||||
icon="download"
|
|
||||||
:underline="false"
|
|
||||||
@click="handleDownloadTemplate"
|
|
||||||
>
|
|
||||||
下载模板
|
下载模板
|
||||||
</el-link>
|
</el-link>
|
||||||
</div>
|
</div>
|
||||||
@@ -485,11 +345,7 @@
|
|||||||
<!-- 弹窗底部操作按钮 -->
|
<!-- 弹窗底部操作按钮 -->
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div style="padding-right: var(--el-dialog-padding-primary)">
|
<div style="padding-right: var(--el-dialog-padding-primary)">
|
||||||
<el-button
|
<el-button type="primary" :disabled="importFormData.files.length === 0" @click="handleImportSubmit">
|
||||||
type="primary"
|
|
||||||
:disabled="importFormData.files.length === 0"
|
|
||||||
@click="handleImportSubmit"
|
|
||||||
>
|
|
||||||
确 定
|
确 定
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="handleCloseImportModal">取 消</el-button>
|
<el-button @click="handleCloseImportModal">取 消</el-button>
|
||||||
@@ -1005,10 +861,10 @@ function fetchPageData(formData: IObject = {}, isRestart = false) {
|
|||||||
.indexAction(
|
.indexAction(
|
||||||
showPagination
|
showPagination
|
||||||
? {
|
? {
|
||||||
[request.pageName]: pagination.currentPage,
|
[request.pageName]: pagination.currentPage,
|
||||||
[request.limitName]: pagination.pageSize,
|
[request.limitName]: pagination.pageSize,
|
||||||
...formData,
|
...formData,
|
||||||
}
|
}
|
||||||
: formData
|
: formData
|
||||||
)
|
)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
@@ -1073,7 +929,7 @@ function saveXlsx(fileData: BlobPart, fileName: string) {
|
|||||||
document.body.removeChild(downloadLink);
|
document.body.removeChild(downloadLink);
|
||||||
window.URL.revokeObjectURL(downloadUrl);
|
window.URL.revokeObjectURL(downloadUrl);
|
||||||
}
|
}
|
||||||
function test(rows: any[]) {}
|
function test(rows: any[]) { }
|
||||||
|
|
||||||
const defaultSelData = ref<IObject[]>([]);
|
const defaultSelData = ref<IObject[]>([]);
|
||||||
// 设置默认选择
|
// 设置默认选择
|
||||||
@@ -1115,17 +971,21 @@ defineExpose({
|
|||||||
:deep(.el-table .el-table__cell) {
|
:deep(.el-table .el-table__cell) {
|
||||||
z-index: inherit;
|
z-index: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-card {
|
.el-card {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-table) {
|
:deep(.el-table) {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
|
||||||
.el-table__header-wrapper {
|
.el-table__header-wrapper {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
z-index: calc(var(--el-table-index) + 2);
|
z-index: calc(var(--el-table-index) + 2);
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-table td.el-table__cell div) {
|
:deep(.el-table td.el-table__cell div) {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,42 @@
|
|||||||
<!-- 文件上传组件 -->
|
<!-- 文件上传组件 -->
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-upload
|
<el-upload v-model:file-list="fileList" :style="props.style" :before-upload="handleBeforeUpload"
|
||||||
v-model:file-list="fileList"
|
:http-request="handleUpload" :on-progress="handleProgress" :on-success="handleSuccess" :on-error="handleError"
|
||||||
:style="props.style"
|
:accept="props.accept" :limit="props.limit" multiple drag :tip="'支持多个文件上传,单个文件不超过 ' + props.maxFileSize + 'MB'">
|
||||||
:before-upload="handleBeforeUpload"
|
<!-- 拖拽上传区域 -->
|
||||||
:http-request="handleUpload"
|
<div>
|
||||||
:on-progress="handleProgress"
|
<el-icon class="el-icon--upload">
|
||||||
:on-success="handleSuccess"
|
<UploadFilled />
|
||||||
:on-error="handleError"
|
</el-icon>
|
||||||
:accept="props.accept"
|
<div class="el-upload__text">
|
||||||
:limit="props.limit"
|
将文件拖到此处,或 <em>点击上传</em>
|
||||||
multiple
|
<br />
|
||||||
>
|
<small>支持格式:{{ props.accept }}</small>
|
||||||
<!-- 上传文件按钮 -->
|
</div>
|
||||||
<el-button type="primary" :disabled="fileList.length >= props.limit">
|
</div>
|
||||||
{{ props.uploadBtnText }}
|
|
||||||
</el-button>
|
|
||||||
|
|
||||||
<!-- 文件列表 -->
|
<!-- 文件列表 -->
|
||||||
<template #file="{ file }">
|
<template #file="{ file }">
|
||||||
<div class="el-upload-list__item-info">
|
<div class="el-upload-list__item-info">
|
||||||
<a class="el-upload-list__item-name" @click="handleDownload(file)">
|
<a class="el-upload-list__item-name" @click="handleDownload(file)">
|
||||||
<el-icon><Document /></el-icon>
|
<el-icon>
|
||||||
|
<Document />
|
||||||
|
</el-icon>
|
||||||
<span class="el-upload-list__item-file-name">{{ file.name }}</span>
|
<span class="el-upload-list__item-file-name">{{ file.name }}</span>
|
||||||
<span class="el-icon--close" @click="handleRemove(file.url!)">
|
<span class="el-icon--close" @click="handleRemove(file.url)">
|
||||||
<el-icon><Close /></el-icon>
|
<el-icon>
|
||||||
|
<Close />
|
||||||
|
</el-icon>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
|
<el-progress :style="{
|
||||||
<el-progress
|
display: showProgress ? 'inline-flex' : 'none',
|
||||||
:style="{
|
width: '100%',
|
||||||
display: showProgress ? 'inline-flex' : 'none',
|
marginTop: '10px',
|
||||||
width: '100%',
|
}" :percentage="progressPercent" />
|
||||||
}"
|
|
||||||
:percentage="progressPercent"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -48,9 +46,14 @@ import {
|
|||||||
UploadProgressEvent,
|
UploadProgressEvent,
|
||||||
UploadRequestOptions,
|
UploadRequestOptions,
|
||||||
} from "element-plus";
|
} from "element-plus";
|
||||||
|
import { Upload, Document, Close } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
import CommonApi, { FileInfo, uploadResponse } from "@/api/account/common";
|
import CommonApi, { FileInfo, uploadResponse } from "@/api/account/common";
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
"upload-success": [fileInfo: string];
|
||||||
|
}>();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
/**
|
/**
|
||||||
* 请求携带的额外参数
|
* 请求携带的额外参数
|
||||||
@@ -96,7 +99,6 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: "上传文件",
|
default: "上传文件",
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 样式
|
* 样式
|
||||||
*/
|
*/
|
||||||
@@ -104,7 +106,7 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => {
|
default: () => {
|
||||||
return {
|
return {
|
||||||
width: "300px",
|
width: "100%",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -147,6 +149,7 @@ function handleBeforeUpload(file: UploadRawFile) {
|
|||||||
ElMessage.warning("上传图片不能大于" + props.maxFileSize + "M");
|
ElMessage.warning("上传图片不能大于" + props.maxFileSize + "M");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
showProgress.value = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,10 +193,13 @@ const handleProgress = (event: UploadProgressEvent) => {
|
|||||||
const handleSuccess = (fileInfo: string) => {
|
const handleSuccess = (fileInfo: string) => {
|
||||||
ElMessage.success("上传成功");
|
ElMessage.success("上传成功");
|
||||||
modelValue.value = [...modelValue.value, fileInfo];
|
modelValue.value = [...modelValue.value, fileInfo];
|
||||||
|
emit("upload-success", fileInfo);
|
||||||
|
showProgress.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleError = (error: any) => {
|
const handleError = (error: any) => {
|
||||||
ElMessage.error("上传失败");
|
ElMessage.error("上传失败");
|
||||||
|
showProgress.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
242
src/components/Upload/GfileUpload.vue
Normal file
242
src/components/Upload/GfileUpload.vue
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
<!-- 文件上传组件 -->
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-upload v-model:file-list="fileList" :style="props.style" :before-upload="handleBeforeUpload"
|
||||||
|
:on-change="handleChange" :accept="accept" multiple drag :tip="'支持多个文件上传,单个文件不超过 ' + props.maxFileSize + 'MB'"
|
||||||
|
:auto-upload="false">
|
||||||
|
<!-- 拖拽上传区域 -->
|
||||||
|
<div>
|
||||||
|
<el-icon class="el-icon--upload">
|
||||||
|
<UploadFilled />
|
||||||
|
</el-icon>
|
||||||
|
<div class="el-upload__text">
|
||||||
|
将文件拖到此处,或 <em>点击上传</em>
|
||||||
|
<br />
|
||||||
|
<small>支持格式:{{ accept }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 文件列表 -->
|
||||||
|
<template #file="{ file }">
|
||||||
|
<div class="el-upload-list__item-info">
|
||||||
|
<a class="el-upload-list__item-name" @click="handleDownload(file)">
|
||||||
|
<el-icon>
|
||||||
|
<Document />
|
||||||
|
</el-icon>
|
||||||
|
<span class="el-upload-list__item-file-name">{{ file.name }}</span>
|
||||||
|
<span class="el-icon--close" @click="handleRemove(file.url)">
|
||||||
|
<el-icon>
|
||||||
|
<Close />
|
||||||
|
</el-icon>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { UploadFilled, Document, Close } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
const emit = defineEmits({
|
||||||
|
"file-selected": null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
/**
|
||||||
|
* 文件上传数量限制
|
||||||
|
*/
|
||||||
|
limit: {
|
||||||
|
type: Number,
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 单个文件上传大小限制(单位MB)
|
||||||
|
*/
|
||||||
|
maxFileSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 上传文件类型
|
||||||
|
*/
|
||||||
|
accept: {
|
||||||
|
type: String,
|
||||||
|
default: "*",
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 选择文件成功的回调函数
|
||||||
|
*/
|
||||||
|
successCallback: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 样式
|
||||||
|
*/
|
||||||
|
style: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
width: "100%",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileList = defineModel({
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件选择变化
|
||||||
|
*/
|
||||||
|
function handleChange(file, fileList) {
|
||||||
|
if (file.status === 'ready') {
|
||||||
|
// 如果限制为1个文件,移除之前的文件,只保留当前新文件
|
||||||
|
if (props.limit === 1 && fileList.length > 1) {
|
||||||
|
fileList.splice(0, fileList.length - 1);
|
||||||
|
}
|
||||||
|
const rawFile = file.raw;
|
||||||
|
// 限制文件类型
|
||||||
|
if (props.accept !== "*") {
|
||||||
|
const acceptedTypes = props.accept.split(',').map(type => type.trim());
|
||||||
|
const isAccepted = acceptedTypes.some(type => {
|
||||||
|
let checkType = type;
|
||||||
|
if (!type.startsWith('.') && !type.includes('/')) {
|
||||||
|
checkType = '.' + type;
|
||||||
|
}
|
||||||
|
if (checkType.startsWith('.')) {
|
||||||
|
return rawFile.name.toLowerCase().endsWith(checkType.toLowerCase());
|
||||||
|
} else if (checkType.includes('*')) {
|
||||||
|
const [main] = checkType.split('/');
|
||||||
|
return rawFile.type.startsWith(main + '/');
|
||||||
|
} else {
|
||||||
|
return rawFile.type === checkType;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!isAccepted) {
|
||||||
|
ElMessage.warning("文件类型不符合要求");
|
||||||
|
// 从 fileList 中移除不符合的文件
|
||||||
|
fileList.splice(fileList.indexOf(file), 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (props.successCallback) {
|
||||||
|
props.successCallback(rawFile);
|
||||||
|
} else {
|
||||||
|
emit("file-selected", rawFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传前校验
|
||||||
|
*/
|
||||||
|
function handleBeforeUpload(file) {
|
||||||
|
// 限制文件大小
|
||||||
|
if (file.size > props.maxFileSize * 1024 * 1024) {
|
||||||
|
ElMessage.warning("上传文件不能大于" + props.maxFileSize + "M");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 限制文件类型
|
||||||
|
if (props.accept !== "*") {
|
||||||
|
const acceptedTypes = props.accept.split(',').map(type => type.trim());
|
||||||
|
const isAccepted = acceptedTypes.some(type => {
|
||||||
|
if (type.startsWith('.')) {
|
||||||
|
return file.name.toLowerCase().endsWith(type.toLowerCase());
|
||||||
|
} else {
|
||||||
|
return file.type === type;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!isAccepted) {
|
||||||
|
ElMessage.warning("文件类型不符合要求");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件
|
||||||
|
*/
|
||||||
|
function handleRemove(fileUrl) {
|
||||||
|
// 从 fileList 中移除
|
||||||
|
fileList.value = fileList.value.filter(f => f.url !== fileUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件
|
||||||
|
*/
|
||||||
|
function handleDownload(file) {
|
||||||
|
// 本地文件,不支持下载
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.el-upload-list__item .el-icon--close {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 5px;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.75;
|
||||||
|
transition: opacity var(--el-transition-duration);
|
||||||
|
transform: translateY(-50%);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-upload-list) {
|
||||||
|
margin: 10px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-upload-list__item) {
|
||||||
|
margin: 5px 0;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f0f0f0;
|
||||||
|
border-color: var(--el-color-primary-light-3);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload-list__item-info {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload-list__item-file-name {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon--close {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
252
src/components/importData/index.vue
Normal file
252
src/components/importData/index.vue
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
<!-- 批量导入数据dialog -->
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="btn_row">
|
||||||
|
<el-button type="primary" icon="Upload" @click="show">批量导入</el-button>
|
||||||
|
<el-button icon="Download" @click="downloadTemplateAjax">下载银收客模板</el-button>
|
||||||
|
</div>
|
||||||
|
<el-dialog title="批量导入" width="800px" v-model="visible" :close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false">
|
||||||
|
<div class="row">
|
||||||
|
<tabHeader v-model="tabActive" :list="tabs" />
|
||||||
|
</div>
|
||||||
|
<div class="row mt14">
|
||||||
|
<div class="import_container" v-if="tabActive == 0">
|
||||||
|
<div class="header_title">第一步:选择模板</div>
|
||||||
|
<div class="row mt14 pb50">
|
||||||
|
<div class="list">
|
||||||
|
<div class="item" :class="{ active: platformActive == index }" v-for="(item, index) in platformList"
|
||||||
|
:key="item.id" @click="selectPlatform(item)">
|
||||||
|
<img class="img" :src="item.img" alt="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="header_title">第二步:上传文件
|
||||||
|
<span>单次仅可上传一个文件</span>
|
||||||
|
</div>
|
||||||
|
<div class="row mt14">
|
||||||
|
<GfileUpload v-model="form.files" :accept="platformList[platformActive]?.file_type || ''" :limit="1"
|
||||||
|
@file-selected="fileSelected" />
|
||||||
|
</div>
|
||||||
|
<div class="row mt14">
|
||||||
|
<div class="footer_wrap">
|
||||||
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" :disabled="!form.files.length" :loading="loading" @click="startImportHandle">
|
||||||
|
<template v-if="!form.files.length">请选择文件</template>
|
||||||
|
<template v-else>开始导入</template>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-table :data="tableData" border stripe v-if="tabActive == 1" height="419px">
|
||||||
|
<el-table-column prop="file_name" label="文件名称" width="300" />
|
||||||
|
<el-table-column prop="created_time" label="导入时间" width="200" />
|
||||||
|
<el-table-column prop="status_text" label="导入状态" width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag v-if="scope.row.status == 0" type="info" disable-transitions>待处理...</el-tag>
|
||||||
|
<el-tag v-else-if="scope.row.status == 1" type="warning" disable-transitions>处理中...</el-tag>
|
||||||
|
<el-tag v-else-if="scope.row.status == 2" type="success" disable-transitions>处理完成</el-tag>
|
||||||
|
<el-tag v-else-if="scope.row.status == -1" type="danger" disable-transitions>导入失败</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="importResult" label="导入结果">
|
||||||
|
<template #default="scope">
|
||||||
|
<template v-if="scope.row.status == 2">
|
||||||
|
<div class="column">
|
||||||
|
<div>
|
||||||
|
<el-text type="success">成功:{{ scope.row.success_num }} 条</el-text>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<el-text type="danger">失败:{{ scope.row.fail_num }} 条</el-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, watch } from 'vue';
|
||||||
|
import { getplatlist, uploadFile, importlist, downloadTemp } from '@/importDataApi/index.js';
|
||||||
|
import GfileUpload from '../Upload/GfileUpload.vue';
|
||||||
|
import tabHeader from '@/views/marketing_center/components/tabHeader.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
type: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 3, // 3商品 4台桌区域 5台桌 6会员 7菜品销售统计 8台桌销售统计 9订单销售统计
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const platformList = ref([]);
|
||||||
|
const platformActive = ref(0);
|
||||||
|
function selectPlatform(item) {
|
||||||
|
form.value.files = [];
|
||||||
|
platformActive.value = platformList.value.findIndex(i => i.id === item.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabs = ref([
|
||||||
|
{ label: '导入数据', name: 'importData' },
|
||||||
|
{ label: '导入记录', name: 'importRecord' },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const tabActive = ref(0)
|
||||||
|
|
||||||
|
watch(tabActive, (newVal) => {
|
||||||
|
if (newVal === 1) {
|
||||||
|
getImportRecord();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
const loading = ref(false);
|
||||||
|
const form = ref({
|
||||||
|
files: [],
|
||||||
|
platform: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
function fileSelected(file) {
|
||||||
|
console.log('fileSelected', file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始导入
|
||||||
|
async function startImportHandle() {
|
||||||
|
try {
|
||||||
|
form.value.platform = platformList.value[platformActive.value]?.id;
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', form.value.files[0].raw);
|
||||||
|
formData.append('shop_id', localStorage.getItem('shopId'));
|
||||||
|
formData.append('type', props.type);
|
||||||
|
formData.append('platform', form.value.platform);
|
||||||
|
loading.value = true;
|
||||||
|
await uploadFile(formData);
|
||||||
|
ElMessage.success('文件上传成功,正在导入数据,请在导入记录中查看导入结果');
|
||||||
|
form.value.files = []
|
||||||
|
tabActive.value = 1
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取平台列表
|
||||||
|
async function getPlatformList() {
|
||||||
|
try {
|
||||||
|
const res = await getplatlist({ plat_type: props.type });
|
||||||
|
platformList.value = res;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入记录
|
||||||
|
const tableData = ref([]);
|
||||||
|
async function getImportRecord() {
|
||||||
|
try {
|
||||||
|
const res = await importlist({ shop_id: localStorage.getItem('shopId') });
|
||||||
|
tableData.value = res;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载模板
|
||||||
|
async function downloadTemplateAjax() {
|
||||||
|
try {
|
||||||
|
const res = await downloadTemp({ plat_type: props.type });
|
||||||
|
window.open(res, '_blank');
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getPlatformList()
|
||||||
|
});
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.btn_row {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
margin-left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header_title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333333;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999999;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
&.mt14 {
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pb50 {
|
||||||
|
padding-bottom: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.import_container {
|
||||||
|
.list {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
width: 122px;
|
||||||
|
height: 42px;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer_wrap {
|
||||||
|
display: flex;
|
||||||
|
gap: 14px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
57
src/importDataApi/index.js
Normal file
57
src/importDataApi/index.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import request from "@/utils/request-import-php";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平台列表
|
||||||
|
* @param {*} data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getplatlist(data) {
|
||||||
|
return request({
|
||||||
|
url: "plat/getplatlist",
|
||||||
|
method: "post",
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入
|
||||||
|
* @param {*} data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function uploadFile(data) {
|
||||||
|
return request({
|
||||||
|
url: "upload",
|
||||||
|
method: "post",
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入记录
|
||||||
|
* @param {*} data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function importlist(data) {
|
||||||
|
return request({
|
||||||
|
url: "plat/importlist",
|
||||||
|
method: "post",
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载模板
|
||||||
|
* @param {*} data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function downloadTemp(data) {
|
||||||
|
return request({
|
||||||
|
url: "plat/downloadTemp",
|
||||||
|
method: "post",
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@@ -63,6 +63,14 @@ export const constantRoutes: RouteRecordRaw[] = [
|
|||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "finance",
|
||||||
|
name: "",
|
||||||
|
component: () => import("@/views/data/finance.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "财务报表"
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "work",
|
path: "work",
|
||||||
name: "workStatistics",
|
name: "workStatistics",
|
||||||
|
|||||||
131
src/utils/request-import-php.js
Normal file
131
src/utils/request-import-php.js
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import router from "@/router";
|
||||||
|
import { getDouyinToken, getToken } from "@/utils/auth";
|
||||||
|
|
||||||
|
// 创建axios实例
|
||||||
|
const service = axios.create({
|
||||||
|
baseURL: import.meta.env.VITE_APP_API_PHP_IMPORT_URL, // api 的 base_url
|
||||||
|
// baseURL: "/import_api", // api 的 base_url
|
||||||
|
timeout: 1000 * 20, // 请求超时时间
|
||||||
|
});
|
||||||
|
|
||||||
|
// request拦截器
|
||||||
|
service.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
if (getToken()) {
|
||||||
|
config.headers["token"] = getToken();
|
||||||
|
}
|
||||||
|
if (!config.headers["Content-Type"]) {
|
||||||
|
config.headers["Content-Type"] = "application/json";
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
// 确保错误回调返回Promise,避免状态异常
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// response 拦截器
|
||||||
|
service.interceptors.response.use(
|
||||||
|
(response) => {
|
||||||
|
const data = response.data;
|
||||||
|
console.log(data);
|
||||||
|
if (data.code == 0) {
|
||||||
|
ElNotification.error({
|
||||||
|
title: data.msg,
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data.code == 439 || data.code == 303) {
|
||||||
|
ElNotification.error({
|
||||||
|
title: "请登录",
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data.code == 4399) {
|
||||||
|
ElNotification.error({
|
||||||
|
title: data.msg,
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
// if (data.code == 1 && !data.data) {
|
||||||
|
// ElNotification.success({
|
||||||
|
// title: data.msg,
|
||||||
|
// duration: 5000
|
||||||
|
// })
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.log(error);
|
||||||
|
if (axios.isCancel(error)) {
|
||||||
|
console.log("请求已取消");
|
||||||
|
ElNotification.error({
|
||||||
|
title: "请求已取消",
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
return Promise.reject("请求已取消");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容blob下载出错json提示
|
||||||
|
if (
|
||||||
|
error.response.data instanceof Blob &&
|
||||||
|
error.response.data.type.toLowerCase().indexOf("json") !== -1
|
||||||
|
) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsText(error.response.data, "utf-8");
|
||||||
|
reader.onload = function (e) {
|
||||||
|
const errorMsg = JSON.parse(reader.result).message;
|
||||||
|
ElNotification.error({
|
||||||
|
title: errorMsg,
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let code = 0;
|
||||||
|
try {
|
||||||
|
code = error.response.data.status;
|
||||||
|
} catch (e) {
|
||||||
|
if (error.toString().indexOf("Error: timeout") !== -1) {
|
||||||
|
ElNotification.error({
|
||||||
|
title: "网络请求超时",
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(code);
|
||||||
|
if (code) {
|
||||||
|
if (code === 401) {
|
||||||
|
// store.dispatch("LogOut").then(() => {
|
||||||
|
// // 用户登录界面提示
|
||||||
|
// Cookies.set("point", 401);
|
||||||
|
// location.reload();
|
||||||
|
// });
|
||||||
|
} else if (code === 403) {
|
||||||
|
router.push({ path: "/401" });
|
||||||
|
} else {
|
||||||
|
const errorMsg = error.response.data.message;
|
||||||
|
if (errorMsg !== undefined) {
|
||||||
|
ElNotification.error({
|
||||||
|
title: errorMsg,
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ElNotification.error({
|
||||||
|
title: "接口请求失败",
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
export default service;
|
||||||
625
src/views/data/finance.vue
Normal file
625
src/views/data/finance.vue
Normal file
@@ -0,0 +1,625 @@
|
|||||||
|
<template>
|
||||||
|
<div class="gyq_container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="between">
|
||||||
|
<el-form :model="queryForm" inline>
|
||||||
|
<el-form-item>
|
||||||
|
<el-date-picker v-model="queryForm.queryDate" type="date" placeholder="选择日期" format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD" :disabled-date="disabledFutureDate" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="Search" :loading="loading" @click="handleQuery">查询</el-button>
|
||||||
|
<el-button @click="handleReset" icon="Refresh" :loading="loading">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-form inline>
|
||||||
|
<el-form-item>
|
||||||
|
<el-select v-model="queryForm.platform" placeholder="选择平台" clearable style="width: 150px;">
|
||||||
|
<el-option v-for="item in platformList" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @click="handleExport" icon="Download">导出</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row" v-loading="loading">
|
||||||
|
<div class="warp" style="width: 400px;">
|
||||||
|
<div class="card">
|
||||||
|
<div class="header_title">营业额</div>
|
||||||
|
<div class="num" style="color: var(--el-color-primary);">
|
||||||
|
<span class="i">¥</span>
|
||||||
|
{{ multiplyAndFormat(tableData.turnover.turnover || 0) }}
|
||||||
|
</div>
|
||||||
|
<div class="intro">营业额</div>
|
||||||
|
<div class="pay_wrap">
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.turnover.wechat || 0 }}</span>
|
||||||
|
<span class="t">微信支付金额</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.turnover.alipay || 0 }}</span>
|
||||||
|
<span class="t">支付宝支付金额</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.turnover.selfScan || 0 }}</span>
|
||||||
|
<span class="t">主扫收款</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.turnover.cash || 0 }}</span>
|
||||||
|
<span class="t">现金收款</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.turnover.recharge || 0 }}</span>
|
||||||
|
<span class="t">充值</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.turnover.owed || 0 }}</span>
|
||||||
|
<span class="t">挂账</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.turnover.balance || 0 }}</span>
|
||||||
|
<span class="t">余额支付</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="order_info">
|
||||||
|
<div class="order_title">
|
||||||
|
<span class="dot"></span>
|
||||||
|
<span class="t">订单</span>
|
||||||
|
</div>
|
||||||
|
<div class="order_lits">
|
||||||
|
<div class="item">
|
||||||
|
<span class="t">订单金额</span>
|
||||||
|
<span class="n">{{ tableData.order.orderAmount || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
<span class="line">|</span>
|
||||||
|
<div class="item">
|
||||||
|
<span class="t">订单总数</span>
|
||||||
|
<span class="n">{{ tableData.order.orderCount || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="header_title">数据统计</div>
|
||||||
|
<div class="pay_wrap">
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.sts.customerCount || 0 }}</span>
|
||||||
|
<span class="t">就餐人数</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.sts.orderCount || 0 }}</span>
|
||||||
|
<span class="t">订单数</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.sts.tableCount || 0 }}</span>
|
||||||
|
<span class="t">桌台数</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.sts.avgPayAmount || 0 }}</span>
|
||||||
|
<div class="t">客单价
|
||||||
|
<el-tooltip class="box-item" effect="dark" content="实付金额(包含现金支付 包含会员支付 包含挂账)/就餐人数没有具体人数时,默认一桌按照1人计算"
|
||||||
|
placement="top">
|
||||||
|
<el-icon color="#666">
|
||||||
|
<QuestionFilled />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.sts.turnoverRate || 0 }}%</span>
|
||||||
|
<div class="t">翻台率
|
||||||
|
<el-tooltip class="box-item" effect="dark" content="(订单数-桌台数)/桌台数*100%" placement="top">
|
||||||
|
<el-icon color="#666">
|
||||||
|
<QuestionFilled />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.sts.profitAmount || 0 }}</span>
|
||||||
|
<div class="t">毛利润
|
||||||
|
<el-tooltip class="box-item" effect="dark" content="(订单实付金额-商品成本)" placement="top">
|
||||||
|
<el-icon color="#666">
|
||||||
|
<QuestionFilled />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.sts.productCostAmount || 0 }}</span>
|
||||||
|
<span class="t">商品成本</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.sts.profitRate || 0 }}%</span>
|
||||||
|
<div class="t">毛利率
|
||||||
|
<el-tooltip class="box-item" effect="dark" content="(订单实付金额-商品成本)/订单实付金额*100%" placement="top">
|
||||||
|
<el-icon color="#666">
|
||||||
|
<QuestionFilled />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.sts.netProfitAmount || 0 }}%</span>
|
||||||
|
<span class="t">净利率</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span class="n">{{ tableData.sts.netProfitRate || 0 }}</span>
|
||||||
|
<span class="t">净利润</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card" style="flex: 1;">
|
||||||
|
<div class="header_title">商家经营数据</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="card" style="flex: 1;">
|
||||||
|
<div class="num" style="color: var(--el-color-success);">
|
||||||
|
<span class="i">¥</span>
|
||||||
|
{{ multiplyAndFormat(tableData.discount.discountAmount || 0) }}
|
||||||
|
</div>
|
||||||
|
<div class="intro">优惠金额</div>
|
||||||
|
<div class="item_list">
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.discount.discountCount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
优惠笔数
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.discount.newConsumerDiscount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
新客立减
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.discount.freeCashAmount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
霸王餐
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.discount.fullMinusAmount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
满减活动
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.discount.couponAmount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
优惠券
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.discount.memberDiscount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
会员折扣
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.discount.pointsDiscountAmount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
积分抵扣金额
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.discount.orderDiscount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
订单改价
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card" style="flex: 1;">
|
||||||
|
<div class="num" style="color: var(--el-color-danger);">
|
||||||
|
<span class="i">¥</span>
|
||||||
|
{{ multiplyAndFormat(tableData.refund.refundAmount || 0) }}
|
||||||
|
</div>
|
||||||
|
<div class="intro">退款金额</div>
|
||||||
|
<div class="item_list">
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.refund.onlineRefundAmount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
线上退款金额
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.refund.cashRefundAmount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
现金退款金额
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.refund.rechargeRefundAmount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
充值退款金额
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.refund.onlineRechargeRefundAmount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
线上充值退款金额
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.refund.cashRechargeRefundAmount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
现金充值退款金额
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.refund.memberRefundAmount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
会员退款金额
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card" style="flex: 1;">
|
||||||
|
<div class="num" style="color: var(--el-color-warning);">
|
||||||
|
<span class="i">¥</span>
|
||||||
|
{{ multiplyAndFormat(tableData.order.orderAmount || 0) }}
|
||||||
|
</div>
|
||||||
|
<div class="intro">订单金额</div>
|
||||||
|
<div class="item_list">
|
||||||
|
<div class="item">
|
||||||
|
<div class="n">
|
||||||
|
{{ tableData.order.orderCount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
订单总数
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import OrderApi from "@/api/order/order";
|
||||||
|
import { downloadFile, multiplyAndFormat } from '@/utils'
|
||||||
|
|
||||||
|
const queryForm = ref({
|
||||||
|
queryDate: dayjs().format('YYYY-MM-DD'), // 查询日期 yyyy-MM-dd
|
||||||
|
platform: 'czg',
|
||||||
|
shopId: localStorage.getItem('shopId') || '',
|
||||||
|
mainShopId: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const platformList = ref([
|
||||||
|
{ value: 'czg', label: '银收客' },
|
||||||
|
{ value: 'MTuan', label: '美团' },
|
||||||
|
{ value: 'CMeMe', label: '菜么么' },
|
||||||
|
{ value: 'KRuYun', label: '客如云' },
|
||||||
|
])
|
||||||
|
|
||||||
|
// 禁用今天以后的日期
|
||||||
|
const disabledFutureDate = (time) => {
|
||||||
|
return dayjs(time).isAfter(dayjs().startOf('day'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询
|
||||||
|
function handleQuery() {
|
||||||
|
getData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
function handleReset() {
|
||||||
|
queryForm.value.queryDate = dayjs().format('YYYY-MM-DD')
|
||||||
|
queryForm.value.platform = 'czg'
|
||||||
|
getData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
async function handleExport() {
|
||||||
|
try {
|
||||||
|
if (!queryForm.value.platform) {
|
||||||
|
ElMessage.error('请选择导出平台')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await OrderApi.financeExport(queryForm.value)
|
||||||
|
downloadFile(res, '财务报表', 'xlsx')
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const tableData = ref({
|
||||||
|
turnover: {},
|
||||||
|
order: {},
|
||||||
|
discount: {},
|
||||||
|
refund: {},
|
||||||
|
sts: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 查询财务报表
|
||||||
|
async function getData() {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const res = await OrderApi.financeSts(queryForm.value)
|
||||||
|
tableData.value = res
|
||||||
|
console.log('tableData.value', tableData.value);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.gyq_container {
|
||||||
|
padding: 14px;
|
||||||
|
|
||||||
|
.gyq_content {
|
||||||
|
padding: 14px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.between {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
gap: 14px;
|
||||||
|
|
||||||
|
&.mt14 {
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.warp {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.header_title {
|
||||||
|
font-size: 16px;
|
||||||
|
color: 333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
background-color: #F8F8F8;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 14px;
|
||||||
|
display: flex;
|
||||||
|
gap: 14px;
|
||||||
|
|
||||||
|
.item_list {
|
||||||
|
margin-top: 14px;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
height: 50px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 14px;
|
||||||
|
|
||||||
|
&:nth-child(odd) {
|
||||||
|
background-color: #F3F7FA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.num {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.i {
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
top: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay_wrap {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
grid-column-gap: 0px;
|
||||||
|
grid-row-gap: 14px;
|
||||||
|
background-color: #F8F8F8;
|
||||||
|
padding: 14px;
|
||||||
|
margin-top: 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.n {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order_info {
|
||||||
|
border-top: 1px solid #ececec;
|
||||||
|
padding-top: 14px;
|
||||||
|
|
||||||
|
.order_title {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
--size: 12px;
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order_lits {
|
||||||
|
display: flex;
|
||||||
|
gap: 14px;
|
||||||
|
margin-top: 4px;
|
||||||
|
|
||||||
|
.line {
|
||||||
|
color: #ececec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
|
||||||
|
.t {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.waterfall {
|
||||||
|
/* 核心属性1:定义瀑布流列数(关键) */
|
||||||
|
column-count: 3;
|
||||||
|
/* 核心属性2:列之间的间距(替代margin,避免卡片间距错乱) */
|
||||||
|
column-gap: 14px;
|
||||||
|
/* 可选:防止卡片内容被列分割(关键!避免卡片跨列断裂) */
|
||||||
|
break-inside: avoid;
|
||||||
|
|
||||||
|
.waterfall-card {
|
||||||
|
/* 必须:适配多列布局,避免卡片宽度溢出 */
|
||||||
|
width: 100%;
|
||||||
|
/* 卡片间距:仅需设置底部外边距(上下间距),左右间距由column-gap控制 */
|
||||||
|
margin-bottom: 14px;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
/* 配合break-inside: avoid,强化卡片不可分割 */
|
||||||
|
page-break-inside: avoid;
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
display: flex;
|
||||||
|
gap: 14px;
|
||||||
|
align-items: center;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
--size: 10px;
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #000;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
padding-left: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
padding-left: 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -52,6 +52,7 @@
|
|||||||
<span>营业</span>
|
<span>营业</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="u-flex" style="flex-wrap: wrap">
|
<div class="u-flex" style="flex-wrap: wrap">
|
||||||
|
<importData :type="9" style="margin-right: 14px;" />
|
||||||
<el-select v-if="isHeadShop == 1 && loginType == 0" v-model="shopId" placeholder="选择分店"
|
<el-select v-if="isHeadShop == 1 && loginType == 0" v-model="shopId" placeholder="选择分店"
|
||||||
style="width: 200px; margin-right: 10px;" @change="shopChange">
|
style="width: 200px; margin-right: 10px;" @change="shopChange">
|
||||||
<el-option v-for="item in branchList" :key="item.shopId" :label="item.shopName" :value="item.shopId" />
|
<el-option v-for="item in branchList" :key="item.shopId" :label="item.shopName" :value="item.shopId" />
|
||||||
@@ -495,6 +496,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import importData from "@/components/importData/index.vue";
|
||||||
import dataSummaryApi from "@/api/order/data-summary";
|
import dataSummaryApi from "@/api/order/data-summary";
|
||||||
import ShopApi from "@/api/account/shop";
|
import ShopApi from "@/api/account/shop";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
@@ -504,6 +506,7 @@ import { formatDateRange } from './utils/index.js'
|
|||||||
import { multiplyAndFormat } from '@/utils/index.js'
|
import { multiplyAndFormat } from '@/utils/index.js'
|
||||||
export default {
|
export default {
|
||||||
name: "home",
|
name: "home",
|
||||||
|
components: { importData },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
multiplyAndFormat,
|
multiplyAndFormat,
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
<span v-if="!downloadLoading">导出Excel</span>
|
<span v-if="!downloadLoading">导出Excel</span>
|
||||||
<span v-else>下载中...</span>
|
<span v-else>下载中...</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<importData :type="7" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@@ -189,6 +190,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import importData from "@/components/importData/index.vue";
|
||||||
import saleSummaryApi from "@/api/order/sale-summary";
|
import saleSummaryApi from "@/api/order/sale-summary";
|
||||||
import categoryApi from "@/api/product/productclassification";
|
import categoryApi from "@/api/product/productclassification";
|
||||||
import ShopApi from "@/api/account/shop";
|
import ShopApi from "@/api/account/shop";
|
||||||
@@ -197,6 +199,7 @@ import { downloadFile, multiplyAndFormat } from "@/utils/index";
|
|||||||
import { formatDateRange } from './utils/index.js'
|
import { formatDateRange } from './utils/index.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: { importData },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
multiplyAndFormat,
|
multiplyAndFormat,
|
||||||
|
|||||||
@@ -27,13 +27,14 @@
|
|||||||
<el-option v-for="item in branchList" :key="item.shopId" :label="item.shopName" :value="item.shopId" />
|
<el-option v-for="item in branchList" :key="item.shopId" :label="item.shopName" :value="item.shopId" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div style="display: flex;">
|
||||||
<el-button type="primary" @click="getTableData">查询</el-button>
|
<el-button type="primary" @click="getTableData">查询</el-button>
|
||||||
<el-button @click="resetHandle">重置</el-button>
|
<el-button @click="resetHandle">重置</el-button>
|
||||||
<el-button icon="download" v-loading="downloadLoading" @click="downloadHandle">
|
<el-button icon="download" v-loading="downloadLoading" @click="downloadHandle">
|
||||||
<span v-if="!downloadLoading">导出Excel</span>
|
<span v-if="!downloadLoading">导出Excel</span>
|
||||||
<span v-else>下载中...</span>
|
<span v-else>下载中...</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<importData :type="8" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -100,6 +101,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import importData from "@/components/importData/index.vue";
|
||||||
import tableSummaryApi from "@/api/order/table-summary";
|
import tableSummaryApi from "@/api/order/table-summary";
|
||||||
import ShopApi from "@/api/account/shop";
|
import ShopApi from "@/api/account/shop";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
@@ -107,6 +109,7 @@ import { downloadFile } from "@/utils/index";
|
|||||||
import { formatDateRange } from './utils/index.js'
|
import { formatDateRange } from './utils/index.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: { importData },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
timeValue: "today",
|
timeValue: "today",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, nextTick } from 'vue'
|
import { ref, onMounted, nextTick, watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// {label: '设置',value: 1}
|
// {label: '设置',value: 1}
|
||||||
@@ -29,10 +29,8 @@ const modelValue = defineModel('modelValue', {
|
|||||||
required: true
|
required: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// 改变索引
|
// 更新active_wrap位置
|
||||||
function changeHandle(index) {
|
function updateActivePosition(index) {
|
||||||
modelValue.value = index
|
|
||||||
|
|
||||||
let left = 0
|
let left = 0
|
||||||
itemsWidth.value.forEach((val, i) => {
|
itemsWidth.value.forEach((val, i) => {
|
||||||
if (i < index) {
|
if (i < index) {
|
||||||
@@ -40,7 +38,12 @@ function changeHandle(index) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
leftValue.value = left + gap.value * index
|
leftValue.value = left + gap.value * index
|
||||||
|
}
|
||||||
|
|
||||||
|
// 改变索引
|
||||||
|
function changeHandle(index) {
|
||||||
|
modelValue.value = index
|
||||||
|
updateActivePosition(index)
|
||||||
emits('change', index)
|
emits('change', index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,10 +60,14 @@ onMounted(() => {
|
|||||||
console.log('itemRefs===', itemRefs.value);
|
console.log('itemRefs===', itemRefs.value);
|
||||||
console.log('itemsWidth===', itemsWidth.value);
|
console.log('itemsWidth===', itemsWidth.value);
|
||||||
|
|
||||||
|
updateActivePosition(modelValue.value)
|
||||||
changeHandle(modelValue.value)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 监听modelValue变化,更新位置
|
||||||
|
watch(modelValue, (newVal) => {
|
||||||
|
updateActivePosition(newVal)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -15,6 +15,9 @@
|
|||||||
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
|
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template> -->
|
</template> -->
|
||||||
|
<template #custom>
|
||||||
|
<importData :type="3" />
|
||||||
|
</template>
|
||||||
<template #type="scope">
|
<template #type="scope">
|
||||||
{{ typeFilter(scope.row[scope.prop]) }}
|
{{ typeFilter(scope.row[scope.prop]) }}
|
||||||
</template>
|
</template>
|
||||||
@@ -170,6 +173,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import importData from "@/components/importData/index.vue";
|
||||||
import UserAPI from "@/api/product/index";
|
import UserAPI from "@/api/product/index";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import type { IObject, IOperatData } from "@/components/CURD/types";
|
import type { IObject, IOperatData } from "@/components/CURD/types";
|
||||||
@@ -184,6 +188,8 @@ import searchConfig from "./indexconfig/search";
|
|||||||
import MyDialog from "@/components/mycomponents/myDialog.vue";
|
import MyDialog from "@/components/mycomponents/myDialog.vue";
|
||||||
import Statistics from "./indexconfig/statistics.vue";
|
import Statistics from "./indexconfig/statistics.vue";
|
||||||
|
|
||||||
|
const importDataRef = ref(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
searchRef,
|
searchRef,
|
||||||
contentRef,
|
contentRef,
|
||||||
@@ -302,6 +308,11 @@ function getTongji(params: IObject | undefined) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显示批量导入
|
||||||
|
function handleUploadClick() {
|
||||||
|
importDataRef.value.show()
|
||||||
|
}
|
||||||
|
|
||||||
// 导出商品
|
// 导出商品
|
||||||
async function handleExportClick() {
|
async function handleExportClick() {
|
||||||
try {
|
try {
|
||||||
@@ -386,6 +397,9 @@ async function handleToolbarClick(name: string) {
|
|||||||
let res = await UserAPI.sync();
|
let res = await UserAPI.sync();
|
||||||
ElMessage.success("操作成功,数据正在后台同步中...");
|
ElMessage.success("操作成功,数据正在后台同步中...");
|
||||||
}
|
}
|
||||||
|
if (name === "upload") {
|
||||||
|
importDataRef.value.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async function confirm() {
|
async function confirm() {
|
||||||
let res = await UserAPI.stockWarning(form.warnLine);
|
let res = await UserAPI.stockWarning(form.warnLine);
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ const contentConfig: IContentConfig<UserPageQuery> = {
|
|||||||
type: "",
|
type: "",
|
||||||
name: "export",
|
name: "export",
|
||||||
auth: "import",
|
auth: "import",
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
cols: [
|
cols: [
|
||||||
// { type: "selection", width: 50, align: "center" },
|
// { type: "selection", width: 50, align: "center" },
|
||||||
|
|||||||
@@ -84,10 +84,10 @@ export default {
|
|||||||
"https://zhyx.eingdong.com/qrcode/api.php?url=https%3A%2F%2Fzhyx.eingdong.com%2Fcopyright%2F%23%2Fpay%3Fid%3D139451580",
|
"https://zhyx.eingdong.com/qrcode/api.php?url=https%3A%2F%2Fzhyx.eingdong.com%2Fcopyright%2F%23%2Fpay%3Fid%3D139451580",
|
||||||
pays: [
|
pays: [
|
||||||
{
|
{
|
||||||
text: "主扫",
|
text: "扫码收款",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "被扫",
|
text: "二维码收款",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
number: "0",
|
number: "0",
|
||||||
|
|||||||
@@ -23,13 +23,18 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|
||||||
<div class="">
|
<div class="" style="display: flex;">
|
||||||
<el-button icon="plus" @click="addEaraShow()">添加区域</el-button>
|
<el-button icon="plus" @click="addEaraShow()">添加区域</el-button>
|
||||||
<el-button type="primary" icon="plus" @click="addTableShow()">添加台桌</el-button>
|
<el-button type="primary" icon="plus" @click="addTableShow()">添加台桌</el-button>
|
||||||
<el-button type="danger" icon="Setting" @click="clearTabDialogRef.show()">清台设置</el-button>
|
<el-button type="danger" icon="Setting" @click="clearTabDialogRef.show()">清台设置</el-button>
|
||||||
<el-button type="primary" icon="download" @click="showDownloadTableCode">
|
<el-button type="primary" icon="download" @click="showDownloadTableCode">
|
||||||
下载桌台码
|
下载桌台码
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<!-- 批量导入 -->
|
||||||
|
<importData :type="5" />
|
||||||
|
<!-- <el-button icon="Upload" @click="importDataRef.show()">
|
||||||
|
批量导入
|
||||||
|
</el-button> -->
|
||||||
<!-- <el-button type="primary" icon="download" @click="downloadShopCpde">
|
<!-- <el-button type="primary" icon="download" @click="downloadShopCpde">
|
||||||
下载店铺码
|
下载店铺码
|
||||||
</el-button> -->
|
</el-button> -->
|
||||||
@@ -205,6 +210,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import importData from "@/components/importData/index.vue";
|
||||||
import status from "./status.js";
|
import status from "./status.js";
|
||||||
import { useUserStore } from "@/store/modules/user";
|
import { useUserStore } from "@/store/modules/user";
|
||||||
const shopUser = useUserStore();
|
const shopUser = useUserStore();
|
||||||
|
|||||||
@@ -29,8 +29,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<page-content ref="contentRef" :content-config="contentConfig" @add-click="handleAddClick"
|
<page-content ref="contentRef" :content-config="contentConfig" @add-click="handleAddClick"
|
||||||
@edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
|
@edit-click="handleEditClick" @export-click="handleExportClick" @upload-click="" @search-click="handleSearchClick"
|
||||||
@toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange">
|
@toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange">
|
||||||
|
<template #custom>
|
||||||
|
<importData ref="importDataRef" :type="6" />
|
||||||
|
</template>
|
||||||
<template #status="scope">
|
<template #status="scope">
|
||||||
<el-link :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
|
<el-link :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
|
||||||
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
|
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
|
||||||
@@ -146,6 +149,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import importData from "@/components/importData/index.vue";
|
||||||
import UserCouponDialog from "./components/user-coupon-dialog.vue";
|
import UserCouponDialog from "./components/user-coupon-dialog.vue";
|
||||||
import GiveCoupon from "./components/give-coupon.vue";
|
import GiveCoupon from "./components/give-coupon.vue";
|
||||||
import usePage from "@/components/CURD/usePage";
|
import usePage from "@/components/CURD/usePage";
|
||||||
@@ -159,6 +163,7 @@ import shopUserApi from "@/api/account/shopUser";
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { ElNotification } from 'element-plus'
|
import { ElNotification } from 'element-plus'
|
||||||
import { downloadFile } from "@/utils/index";
|
import { downloadFile } from "@/utils/index";
|
||||||
|
|
||||||
const editMoneyModalRef = ref(null);
|
const editMoneyModalRef = ref(null);
|
||||||
const userCouponDialogRef = ref(null);
|
const userCouponDialogRef = ref(null);
|
||||||
const GiveCouponRef = ref(null);
|
const GiveCouponRef = ref(null);
|
||||||
@@ -250,6 +255,8 @@ function handleToolbarClick(name) {
|
|||||||
console.log(name);
|
console.log(name);
|
||||||
if (name === "custom1") {
|
if (name === "custom1") {
|
||||||
ElMessage.success("点击了自定义1按钮");
|
ElMessage.success("点击了自定义1按钮");
|
||||||
|
} else if (name === "upload") {
|
||||||
|
importDataRef.value.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,13 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
|||||||
target: env.VITE_APP_API_URL,
|
target: env.VITE_APP_API_URL,
|
||||||
rewrite: (path) => path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
|
rewrite: (path) => path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
|
||||||
},
|
},
|
||||||
|
// 代理 /dev-api 的请求
|
||||||
|
'/import_api': {
|
||||||
|
changeOrigin: true,
|
||||||
|
// 代理目标地址:https://api.youlai.tech
|
||||||
|
target: env.VITE_APP_API_PHP_IMPORT_URL,
|
||||||
|
rewrite: (path) => path.replace(new RegExp("^" + env.VITE_APP_API_PHP_IMPORT_URL), ""),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|||||||
Reference in New Issue
Block a user