mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-06 17:09:08 +08:00
refactor(seller): 列表页抽离 useTable 重构与通用 UI 规范优化
- 抽离通用 useTable composable,并迁移 13 个典型列表页(采购/报损/订单/售后/统计/营销等) - 增加表格与树组件全局空状态 (el-empty) 样式规范 - 补齐视图图片语义化 alt,并添加全局图片加载异常 SVG 占位兜底 - 替换遗留的 $Message 兼容语法为 ElMessage - 将 WAP 店铺装修模板的 Options API 统一为 defineOptions - 优化数据报表卡片视觉样式,并移除失效的品牌筛选选项 - 清理冗余依赖 vuex,将 sass-loader 调整至 devDependencies
This commit is contained in:
@@ -26,7 +26,6 @@
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^4.0.3",
|
||||
"sass": "^1.63.6",
|
||||
"sass-loader": "^10.4.1",
|
||||
"swiper": "^6.3.5",
|
||||
"uuid": "^8.3.2",
|
||||
"vue": "^3.5.35",
|
||||
@@ -39,7 +38,6 @@
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@vue/cli-plugin-babel": "^5.0.9",
|
||||
"@vue/cli-plugin-router": "^5.0.9",
|
||||
"@vue/cli-plugin-vuex": "^5.0.9",
|
||||
"@vue/cli-service": "^5.0.9",
|
||||
"@vue/compiler-sfc": "^3.5.35",
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
@@ -49,6 +47,7 @@
|
||||
"globals": "^17.13.0",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^11.1.4",
|
||||
"sass-loader": "^10.4.1",
|
||||
"style-loader": "^3.3.4",
|
||||
"style-resources-loader": "^1.5.0",
|
||||
"unplugin-vue-components": "0.27.5",
|
||||
|
||||
@@ -5,6 +5,7 @@ import App from "./App.vue";
|
||||
import { router } from "./router/index";
|
||||
import { setupElementPlus } from "@/plugins/element";
|
||||
import { setupLegacyMessage } from "@/utils/message";
|
||||
import { setupImgFallback } from "@/utils/imgFallback";
|
||||
import liliDialog from "@/views/lili-dialog";
|
||||
import PriceColorScheme from "@/components/price-color-scheme.vue";
|
||||
import { install as installVueQr } from "vue-qr";
|
||||
@@ -39,6 +40,7 @@ const app = createApp(App);
|
||||
|
||||
setupElementPlus(app);
|
||||
setupLegacyMessage(app);
|
||||
setupImgFallback();
|
||||
installVueQr(app);
|
||||
|
||||
app.use(router);
|
||||
|
||||
@@ -112,3 +112,30 @@
|
||||
.el-overlay.is-message-box {
|
||||
z-index: 4000 !important;
|
||||
}
|
||||
|
||||
/* 统一空状态:表格 / 树 / 列表无数据时的展示(未自定义 #empty 插槽的页面均生效) */
|
||||
.el-table__empty-block {
|
||||
min-height: 160px;
|
||||
}
|
||||
|
||||
.el-table__empty-text,
|
||||
.el-tree__empty-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 1.5;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin-bottom: 8px;
|
||||
opacity: 0.7;
|
||||
background: no-repeat center / contain
|
||||
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64' fill='none' stroke='%23c0c4cc' stroke-width='2' stroke-linejoin='round'><path d='M8 36l8-20h32l8 20v12a4 4 0 0 1-4 4H12a4 4 0 0 1-4-4V36z'/><path d='M8 36h16a8 8 0 0 0 16 0h16'/></svg>");
|
||||
}
|
||||
}
|
||||
|
||||
31
seller/src/utils/imgFallback.js
Normal file
31
seller/src/utils/imgFallback.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 全局图片加载失败占位:
|
||||
* 在捕获阶段监听 <img> 的 error 事件(error 不冒泡),失败时替换为内联 SVG 占位图。
|
||||
* - 每张图片只替换一次,占位图自身加载失败不会再次触发,避免死循环
|
||||
* - 空 src 不处理
|
||||
* - 需要自行处理 error 的图片可添加 data-no-fallback 属性跳过
|
||||
*/
|
||||
const PLACEHOLDER =
|
||||
"data:image/svg+xml;utf8," +
|
||||
encodeURIComponent(
|
||||
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64' fill='none' stroke='#c0c4cc' stroke-width='2' stroke-linejoin='round'>" +
|
||||
"<rect width='64' height='64' fill='#f5f7fa' stroke='none'/>" +
|
||||
"<rect x='14' y='18' width='36' height='28' rx='3'/>" +
|
||||
"<circle cx='25' cy='28' r='3'/>" +
|
||||
"<path d='M14 42l10-9 8 7 6-5 12 9'/></svg>"
|
||||
);
|
||||
|
||||
export function setupImgFallback() {
|
||||
document.addEventListener(
|
||||
"error",
|
||||
(event) => {
|
||||
const el = event.target;
|
||||
if (!el || el.tagName !== "IMG") return;
|
||||
if (el.dataset.noFallback !== undefined || el.dataset.fallbackApplied) return;
|
||||
if (!el.getAttribute("src")) return;
|
||||
el.dataset.fallbackApplied = "1";
|
||||
el.src = PLACEHOLDER;
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
119
seller/src/utils/useTable.js
Normal file
119
seller/src/utils/useTable.js
Normal file
@@ -0,0 +1,119 @@
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
|
||||
/**
|
||||
* 分页列表通用逻辑:loading / data / total / searchForm / 查询 / 重置 / 刷新。
|
||||
*
|
||||
* 用法:
|
||||
* const { loading, data, total, searchForm, getDataList, handleSearch, resetSearch } =
|
||||
* useTable(getXxxPage, { form: { name: "", status: "" } });
|
||||
*
|
||||
* <el-table v-loading="loading" :data="data" />
|
||||
* <el-pagination
|
||||
* v-model:current-page="searchForm.pageNumber"
|
||||
* v-model:page-size="searchForm.pageSize"
|
||||
* :total="total"
|
||||
* @current-change="getDataList"
|
||||
* @size-change="getDataList"
|
||||
* />
|
||||
*
|
||||
* @param {(params: object) => Promise<any>} fetcher 请求函数,入参为 searchForm
|
||||
* @param {object} [options]
|
||||
* @param {object} [options.form] 查询条件默认值(会与分页/排序默认值合并)
|
||||
* @param {number} [options.pageSize=10] 默认每页条数
|
||||
* @param {string} [options.sort] 默认排序字段
|
||||
* @param {string} [options.order] 默认排序方向
|
||||
* @param {boolean} [options.immediate=true] 挂载后是否自动查询
|
||||
* @param {(res: any) => ({records: any[], total: number}|null|undefined)} [options.resolve]
|
||||
* 从响应中取出 { records, total };返回空表示请求失败(保持原数据)。
|
||||
* 默认:res.success 为真时取 res.result.records / res.result.total
|
||||
* @param {(params: object) => object} [options.beforeRequest] 请求前加工参数(返回新对象,不影响 searchForm)
|
||||
* @param {(res: any, parsed: {records: any[], total: number}) => void} [options.onSuccess]
|
||||
* 查询成功且数据已更新后回调(仅最后一次请求触发),可用于刷新状态计数等
|
||||
*/
|
||||
export function useTable(fetcher, options = {}) {
|
||||
const {
|
||||
form = {},
|
||||
pageSize = 10,
|
||||
sort,
|
||||
order,
|
||||
immediate = true,
|
||||
resolve = defaultResolve,
|
||||
beforeRequest,
|
||||
onSuccess,
|
||||
} = options;
|
||||
|
||||
const loading = ref(false);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
const initialForm = { ...form };
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize,
|
||||
...(sort ? { sort } : {}),
|
||||
...(order ? { order } : {}),
|
||||
...form,
|
||||
});
|
||||
|
||||
// 只采纳最后一次请求的结果,避免快速切换页码/条件时旧响应覆盖新数据
|
||||
let requestId = 0;
|
||||
|
||||
function getDataList() {
|
||||
const current = ++requestId;
|
||||
loading.value = true;
|
||||
const params = beforeRequest ? beforeRequest({ ...searchForm }) : searchForm;
|
||||
return Promise.resolve(fetcher(params))
|
||||
.then((res) => {
|
||||
if (current !== requestId) return res;
|
||||
const parsed = resolve(res);
|
||||
if (parsed) {
|
||||
data.value = parsed.records || [];
|
||||
total.value = parsed.total || 0;
|
||||
if (onSuccess) onSuccess(res, parsed);
|
||||
}
|
||||
return res;
|
||||
})
|
||||
.finally(() => {
|
||||
if (current === requestId) loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
/** 以当前条件从第 1 页重新查询 */
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
return getDataList();
|
||||
}
|
||||
|
||||
/** 恢复查询条件默认值(保留当前每页条数)并从第 1 页查询 */
|
||||
function resetSearch() {
|
||||
Object.assign(searchForm, initialForm);
|
||||
return handleSearch();
|
||||
}
|
||||
|
||||
/** 保持当前页刷新(如删除/修改后) */
|
||||
function refresh() {
|
||||
return getDataList();
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
onMounted(getDataList);
|
||||
}
|
||||
|
||||
return {
|
||||
loading,
|
||||
data,
|
||||
total,
|
||||
searchForm,
|
||||
getDataList,
|
||||
handleSearch,
|
||||
resetSearch,
|
||||
refresh,
|
||||
};
|
||||
}
|
||||
|
||||
function defaultResolve(res) {
|
||||
if (res && res.success && res.result) {
|
||||
return { records: res.result.records, total: res.result.total };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="forget-password" @click="hideVerify">
|
||||
<div style="height: 50px"></div>
|
||||
<div class="logo-box">
|
||||
<img :src="logoImg" width="150" @click="$router.push('/')" />
|
||||
<img alt="店铺 Logo" :src="logoImg" width="150" @click="$router.push('/')" />
|
||||
<div>修改密码</div>
|
||||
</div>
|
||||
<div class="login-container">
|
||||
@@ -119,7 +119,7 @@
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { Iphone, Message, Lock } from "@element-plus/icons-vue";
|
||||
import { Message as $Message } from "@/utils/message";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { md5 } from "@/utils/md5.js";
|
||||
import * as RegExp from "@/utils/regular.js";
|
||||
import * as apiLogin from "@/api/index.js";
|
||||
@@ -188,7 +188,7 @@ function next() {
|
||||
if (res.success) {
|
||||
step.value = 1;
|
||||
} else {
|
||||
$Message.warning(res.message);
|
||||
ElMessage.warning(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -203,7 +203,7 @@ function handleSubmit() {
|
||||
if (valid) {
|
||||
const params = JSON.parse(JSON.stringify(form));
|
||||
if (params.password !== params.oncePasd) {
|
||||
$Message.warning("两次输入密码不一致");
|
||||
ElMessage.warning("两次输入密码不一致");
|
||||
return;
|
||||
}
|
||||
params.mobile = formFirst.mobile;
|
||||
@@ -216,7 +216,7 @@ function handleSubmit() {
|
||||
.then((res) => {
|
||||
loading1.value = false;
|
||||
if (res.success) {
|
||||
$Message.success("修改密码成功");
|
||||
ElMessage.success("修改密码成功");
|
||||
router.push("login");
|
||||
}
|
||||
})
|
||||
@@ -230,11 +230,11 @@ function handleSubmit() {
|
||||
function sendCode() {
|
||||
if (time.value === 60) {
|
||||
if (formFirst.mobile === "") {
|
||||
$Message.warning("请先填写手机号");
|
||||
ElMessage.warning("请先填写手机号");
|
||||
return;
|
||||
}
|
||||
if (!verifyStatus.value) {
|
||||
$Message.warning("请先完成安全验证");
|
||||
ElMessage.warning("请先完成安全验证");
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
@@ -244,7 +244,7 @@ function sendCode() {
|
||||
sendSms(params)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
$Message.success("验证码发送成功");
|
||||
ElMessage.success("验证码发送成功");
|
||||
interval.value = setInterval(() => {
|
||||
time.value--;
|
||||
if (time.value === 0) {
|
||||
@@ -257,7 +257,7 @@ function sendCode() {
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
$Message.warning(res.message);
|
||||
ElMessage.warning(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="main" :class="{ 'menu-collapsed': !showSubMenu }">
|
||||
<div class="sidebar-menu-con menu-bar">
|
||||
<div class="logo-con">
|
||||
<img :src="storeSideLogo" key="max-logo" />
|
||||
<img alt="店铺 Logo" :src="storeSideLogo" key="max-logo" />
|
||||
</div>
|
||||
<shrinkable-menu />
|
||||
</div>
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
<el-table-column prop="id" label="商品ID" width="200" show-overflow-tooltip />
|
||||
<el-table-column label="商品图片" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<img
|
||||
<img alt="商品图片"
|
||||
v-if="row && row.original"
|
||||
:src="row.original"
|
||||
style="height: 50px; width: 50px; object-fit: cover"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div ref="rootEl">
|
||||
<el-dialog v-model="visible" title="预览图片">
|
||||
<img v-if="visible" :src="previewPicture" style="width: 100%">
|
||||
<img alt="预览图片" v-if="visible" :src="previewPicture" style="width: 100%">
|
||||
</el-dialog>
|
||||
<div class="goods-publish-page">
|
||||
<div ref="sectionNav" class="goods-section-nav">
|
||||
@@ -111,7 +111,7 @@
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="demo-upload-list">
|
||||
<img :src="element" @error="handleGoodsPictureError($event, element)" />
|
||||
<img alt="商品图片" data-no-fallback :src="element" @error="handleGoodsPictureError($event, element)" />
|
||||
<div class="demo-upload-list-cover">
|
||||
<div>
|
||||
<el-button link type="primary" @click="handleViewGoodsPicture(element)">预览</el-button>
|
||||
@@ -139,7 +139,7 @@
|
||||
<el-button @click="handleCLickImg('goodsGalleryFiles')" type="primary">上传图片</el-button>
|
||||
</div>
|
||||
<el-dialog v-model="goodsPictureVisible" title="View Image">
|
||||
<img v-if="goodsPictureVisible" :src="previewGoodsPicture" style="width: 100%" />
|
||||
<img alt="商品图片预览" v-if="goodsPictureVisible" :src="previewGoodsPicture" style="width: 100%" />
|
||||
</el-dialog>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@@ -186,7 +186,7 @@
|
||||
<el-form-item :label="contentImage" class="form-item-view-el">
|
||||
<!-- {{item.url}} -->
|
||||
<div v-for="(item, index) in listImages.images" :key="index" style="width:100%;display:flex;">
|
||||
<img :src="item.url" style="width:100px;flex:1;margin-top:10px;cursor:pointer;"
|
||||
<img alt="图片" :src="item.url" style="width:100px;flex:1;margin-top:10px;cursor:pointer;"
|
||||
@click="handleView(item.url)" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -415,7 +415,7 @@
|
||||
class="sku-upload-list"
|
||||
style="width: 180px;height: 140px"
|
||||
>
|
||||
<img :src="element" style="width: 180px;height: 140px" />
|
||||
<img alt="商品图片" :src="element" style="width: 180px;height: 140px" />
|
||||
<div class="sku-upload-list-cover">
|
||||
<div style="margin-top: 50px">
|
||||
<el-icon :size="25" style="cursor:pointer;margin-right:8px" @click="handleView(element)"><ZoomIn /></el-icon>
|
||||
|
||||
@@ -126,7 +126,7 @@ import { sendSms } from "@/api/common.js";
|
||||
import { login, storeSmsLogin, userMsg } from "@/api/index";
|
||||
import util from "@/libs/util.js";
|
||||
import { setStore } from "@/libs/storage";
|
||||
import { Message as $Message } from "@/utils/message";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { md5 } from "@/utils/md5.js";
|
||||
import Footer from "@/views/main-components/footer";
|
||||
import Header from "@/views/main-components/header";
|
||||
@@ -238,7 +238,7 @@ function afterLogin(res) {
|
||||
|
||||
function sendCode() {
|
||||
if (!isMobileValid.value) {
|
||||
$Message.warning("请输入正确的手机号");
|
||||
ElMessage.warning("请输入正确的手机号");
|
||||
return;
|
||||
}
|
||||
if (!verifyStatus.value) {
|
||||
@@ -255,7 +255,7 @@ function sendCode() {
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
smsCodeSent.value = true;
|
||||
$Message.success("验证码发送成功");
|
||||
ElMessage.success("验证码发送成功");
|
||||
interval = setInterval(() => {
|
||||
time.value--;
|
||||
if (time.value === 0) {
|
||||
@@ -269,7 +269,7 @@ function sendCode() {
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
$Message.warning(res.message);
|
||||
ElMessage.warning(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row class="header">
|
||||
<img class="logo" :src="storeSideLogo" />
|
||||
<img alt="店铺 Logo" class="logo" :src="storeSideLogo" />
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -105,7 +105,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, watch } from "vue";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { useRoute } from "vue-router";
|
||||
import * as API_Order from "@/api/order";
|
||||
import vueQr from "vue-qr";
|
||||
@@ -115,15 +116,14 @@ defineOptions({ name: "orderComplaint" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
const { loading, data, total, searchForm, getDataList } = useTable(API_Order.getOrderComplain, {
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
immediate: false,
|
||||
form: {
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
},
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
// searchFormRef:模板 ref 改名避免与 searchForm 数据互相覆盖
|
||||
const searchFormRef = ref(null);
|
||||
@@ -178,20 +178,6 @@ function handleSearch() {
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Order.getOrderComplain(searchForm)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function detail(v) {
|
||||
customRouterPush({
|
||||
name: "order-complaint-detail",
|
||||
|
||||
@@ -154,7 +154,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { useRoute } from "vue-router";
|
||||
import * as API_Order from "@/api/order";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
@@ -163,25 +164,24 @@ defineOptions({ name: "after-sale-order" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
const { loading, data, total, searchForm, getDataList } = useTable(API_Order.getAfterSaleOrderPage, {
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
serviceType: "RETURN_GOODS",
|
||||
orderSn: "",
|
||||
memberName: "",
|
||||
serviceStatus: "",
|
||||
storeName: "",
|
||||
sn: "",
|
||||
keywords: "",
|
||||
immediate: false,
|
||||
form: {
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
serviceType: "RETURN_GOODS",
|
||||
orderSn: "",
|
||||
memberName: "",
|
||||
serviceStatus: "",
|
||||
storeName: "",
|
||||
sn: "",
|
||||
keywords: "",
|
||||
},
|
||||
});
|
||||
const selectDate = ref(null);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const currentStatus = ref("");
|
||||
const afterSaleNumData = ref({});
|
||||
|
||||
@@ -284,16 +284,6 @@ function selectDateRange(v) {
|
||||
searchForm.endDate = "";
|
||||
}
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Order.getAfterSaleOrderPage(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function getAfterSaleNumData() {
|
||||
const { serviceStatus, ...searchParams } = searchForm;
|
||||
API_Order.getAfterSaleNumVO(searchParams).then((res) => {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<dt>{{ isRefundOnly ? '退款商品' : '退货商品' }}</dt>
|
||||
<dd>
|
||||
<div>
|
||||
<img :src="afterSaleInfo.goodsImage" style="height: 60px">
|
||||
<img alt="商品图片" :src="afterSaleInfo.goodsImage" style="height: 60px">
|
||||
</div>
|
||||
<a @click="linkTo(afterSaleInfo.goodsId, afterSaleInfo.skuId)">{{ afterSaleInfo.goodsName }}
|
||||
</a><br>
|
||||
@@ -59,7 +59,7 @@
|
||||
<dd v-if="afterSaleImage == ''">暂无凭证</dd>
|
||||
<dd v-else>
|
||||
<div class="div-img" v-for="(item, index) in afterSaleImage" :key="index">
|
||||
<img class="complain-img" :src="item" />
|
||||
<img alt="投诉凭证" class="complain-img" :src="item" />
|
||||
</div>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
@@ -237,7 +237,7 @@
|
||||
<tr v-for="row in data" :key="row.sn || row.id">
|
||||
<td>
|
||||
<div class="order-goods-cell">
|
||||
<img :src="row.image" class="order-goods-img" />
|
||||
<img alt="商品图片" :src="row.image" class="order-goods-img" />
|
||||
<div class="order-goods-meta">
|
||||
<div class="div-zoom">
|
||||
<a class="link-text" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
|
||||
|
||||
@@ -157,7 +157,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { useRoute } from "vue-router";
|
||||
import { CopyDocument } from "@element-plus/icons-vue";
|
||||
import * as API_Order from "@/api/order";
|
||||
@@ -169,25 +170,24 @@ defineOptions({ name: "orderList" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
const { loading, data, total, searchForm, getDataList } = useTable(API_Order.getOrderList, {
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
orderType: "",
|
||||
orderSn: "",
|
||||
keywords: "",
|
||||
goodsType: "",
|
||||
orderStatus: "",
|
||||
paymentMethod: "",
|
||||
orderPromotionType: "",
|
||||
immediate: false,
|
||||
form: {
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
orderType: "",
|
||||
orderSn: "",
|
||||
keywords: "",
|
||||
goodsType: "",
|
||||
orderStatus: "",
|
||||
paymentMethod: "",
|
||||
orderPromotionType: "",
|
||||
},
|
||||
});
|
||||
const selectDate = ref(null);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const orderNumData = ref({});
|
||||
const currentStatus = ref("ALL");
|
||||
|
||||
@@ -362,20 +362,6 @@ function selectDateRange(v) {
|
||||
searchForm.endDate = "";
|
||||
}
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Order.getOrderList(searchForm)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function detail(v, openFulfill = false) {
|
||||
customRouterPush({
|
||||
name: "order-detail",
|
||||
|
||||
@@ -127,29 +127,29 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import * as API_Order from "@/api/order";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
|
||||
defineOptions({ name: "fictitiousOrderList" });
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
const { loading, data, total, searchForm, getDataList } = useTable(API_Order.getOrderList, {
|
||||
pageSize: 20,
|
||||
sort: "",
|
||||
order: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
orderType: "VIRTUAL",
|
||||
orderSn: "",
|
||||
buyerName: "",
|
||||
orderStatus: "",
|
||||
immediate: false,
|
||||
form: {
|
||||
sort: "",
|
||||
order: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
orderType: "VIRTUAL",
|
||||
orderSn: "",
|
||||
buyerName: "",
|
||||
orderStatus: "",
|
||||
},
|
||||
});
|
||||
const selectDate = ref(null);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const orderStatus = ref([
|
||||
{ title: "全部", value: "" },
|
||||
{ title: "未付款", value: "UNPAID" },
|
||||
@@ -194,16 +194,6 @@ function selectDateRange(v) {
|
||||
searchForm.endDate = v[1];
|
||||
}
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Order.getOrderList(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function confirmPrice(v) {
|
||||
Modal.confirm({
|
||||
title: "确认收款",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { ElMessageBox } from "element-plus";
|
||||
import {
|
||||
getDamageReportPage,
|
||||
@@ -112,16 +113,21 @@ defineOptions({ name: "procurement-damage-report-list" });
|
||||
// searchFormRef:模板 ref 改名避免与顶层变量互相覆盖
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const dateRange = ref([]);
|
||||
const currentStatus = ref("ALL");
|
||||
const statusCountData = ref({});
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
status: "",
|
||||
const {
|
||||
loading,
|
||||
data,
|
||||
total,
|
||||
searchForm,
|
||||
getDataList,
|
||||
handleSearch: search,
|
||||
resetSearch: reset,
|
||||
} = useTable(getDamageReportPage, {
|
||||
form: { status: "" },
|
||||
immediate: false,
|
||||
beforeRequest: (p) => buildSearchParams(true, p),
|
||||
});
|
||||
|
||||
const statusTabs = computed(() => {
|
||||
@@ -146,8 +152,8 @@ function init() {
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function buildSearchParams(includeStatus = true) {
|
||||
const params = { ...searchForm.value };
|
||||
function buildSearchParams(includeStatus = true, base = searchForm) {
|
||||
const params = { ...base };
|
||||
if (!includeStatus) {
|
||||
delete params.status;
|
||||
}
|
||||
@@ -159,17 +165,15 @@ function buildSearchParams(includeStatus = true) {
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getStatusCount();
|
||||
getDataList();
|
||||
search();
|
||||
}
|
||||
|
||||
function resetSearch() {
|
||||
searchForm.value = { pageNumber: 1, pageSize: 10, status: "" };
|
||||
dateRange.value = [];
|
||||
currentStatus.value = "ALL";
|
||||
getStatusCount();
|
||||
getDataList();
|
||||
reset();
|
||||
}
|
||||
|
||||
function onStatusTabClick(tab) {
|
||||
@@ -178,13 +182,13 @@ function onStatusTabClick(tab) {
|
||||
|
||||
function statusClick(name) {
|
||||
if (name === "ALL" || name === "" || name === undefined) {
|
||||
searchForm.value.status = "";
|
||||
searchForm.status = "";
|
||||
currentStatus.value = "ALL";
|
||||
} else {
|
||||
searchForm.value.status = name;
|
||||
searchForm.status = name;
|
||||
currentStatus.value = name;
|
||||
}
|
||||
searchForm.value.pageNumber = 1;
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
@@ -196,20 +200,6 @@ function getStatusCount() {
|
||||
});
|
||||
}
|
||||
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getDamageReportPage(buildSearchParams(true))
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function refreshList() {
|
||||
getStatusCount();
|
||||
getDataList();
|
||||
|
||||
@@ -49,56 +49,27 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { getProcurementInboundPage } from "@/api/procurement";
|
||||
import { formatMoney } from "../constants";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
|
||||
defineOptions({ name: "procurement-inbound-list" });
|
||||
|
||||
const loading = ref(false);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
inboundSn: "",
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
});
|
||||
const {
|
||||
loading,
|
||||
data,
|
||||
total,
|
||||
searchForm,
|
||||
getDataList,
|
||||
handleSearch,
|
||||
resetSearch,
|
||||
} = useTable(getProcurementInboundPage, { form: { inboundSn: "" }, sort: "createTime", order: "desc" });
|
||||
|
||||
// searchFormRef:模板 ref 改名避免与顶层变量互相覆盖
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
getDataList();
|
||||
});
|
||||
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function resetSearch() {
|
||||
searchForm.inboundSn = "";
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getProcurementInboundPage(searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function goCreate() {
|
||||
customRouterPush({ name: "procurement-inbound-operation" });
|
||||
}
|
||||
|
||||
@@ -34,39 +34,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { createInventoryCount, getInventoryCountPage } from "@/api/procurement";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
|
||||
defineOptions({ name: "procurement-inventory-count-list" });
|
||||
|
||||
const loading = ref(false);
|
||||
const creating = ref(false);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getDataList();
|
||||
});
|
||||
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getInventoryCountPage(searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
const { loading, data, total, searchForm, getDataList } = useTable(getInventoryCountPage);
|
||||
|
||||
function handleCreate() {
|
||||
Modal.confirm({
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { useRoute } from "vue-router";
|
||||
import {
|
||||
getProcurementOrderPage,
|
||||
@@ -117,20 +118,21 @@ defineOptions({ name: "procurement-purchase-order-list" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const loading = ref(false);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const createRange = ref([]);
|
||||
const currentStatus = ref("ALL");
|
||||
const statusCountData = ref({});
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
orderSn: "",
|
||||
goodsName: "",
|
||||
status: "",
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
const {
|
||||
loading,
|
||||
data,
|
||||
total,
|
||||
searchForm,
|
||||
getDataList,
|
||||
handleSearch: search,
|
||||
resetSearch: reset,
|
||||
} = useTable(getProcurementOrderPage, {
|
||||
form: { orderSn: "", goodsName: "", status: "" }, sort: "createTime", order: "desc",
|
||||
immediate: false,
|
||||
beforeRequest: (p) => buildSearchParams(true, p),
|
||||
});
|
||||
|
||||
// searchFormRef:模板 ref 改名避免与顶层变量互相覆盖
|
||||
@@ -153,7 +155,7 @@ onMounted(() => {
|
||||
const status = route.query.status;
|
||||
if (status) {
|
||||
currentStatus.value = status;
|
||||
searchForm.value.status = status;
|
||||
searchForm.status = status;
|
||||
}
|
||||
init();
|
||||
});
|
||||
@@ -163,8 +165,8 @@ function init() {
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function buildSearchParams(includeStatus = true) {
|
||||
const params = { ...searchForm.value };
|
||||
function buildSearchParams(includeStatus = true, base = searchForm) {
|
||||
const params = { ...base };
|
||||
if (!includeStatus) {
|
||||
delete params.status;
|
||||
}
|
||||
@@ -176,25 +178,15 @@ function buildSearchParams(includeStatus = true) {
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getStatusCount();
|
||||
getDataList();
|
||||
search();
|
||||
}
|
||||
|
||||
function resetSearch() {
|
||||
searchForm.value = {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
orderSn: "",
|
||||
goodsName: "",
|
||||
status: "",
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
};
|
||||
createRange.value = [];
|
||||
currentStatus.value = "ALL";
|
||||
getStatusCount();
|
||||
getDataList();
|
||||
reset();
|
||||
}
|
||||
|
||||
function onStatusTabClick(tab) {
|
||||
@@ -203,13 +195,13 @@ function onStatusTabClick(tab) {
|
||||
|
||||
function statusClick(name) {
|
||||
if (name === "ALL" || name === "" || name === undefined) {
|
||||
searchForm.value.status = "";
|
||||
searchForm.status = "";
|
||||
currentStatus.value = "ALL";
|
||||
} else {
|
||||
searchForm.value.status = name;
|
||||
searchForm.status = name;
|
||||
currentStatus.value = name;
|
||||
}
|
||||
searchForm.value.pageNumber = 1;
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
@@ -221,20 +213,6 @@ function getStatusCount() {
|
||||
});
|
||||
}
|
||||
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getProcurementOrderPage(buildSearchParams(true))
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function refreshList() {
|
||||
getStatusCount();
|
||||
getDataList();
|
||||
|
||||
@@ -51,45 +51,32 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { useRouter } from "vue-router";
|
||||
import { getFlashDiscountList, updateFlashDiscountStatus } from "@/api/promotion.js";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const loading = ref(false);
|
||||
const total = ref(0);
|
||||
const data = ref([]);
|
||||
const searchForm = reactive({ pageNumber: 1, pageSize: 20, sort: "createTime", order: "desc" });
|
||||
const {
|
||||
loading,
|
||||
data,
|
||||
total,
|
||||
searchForm,
|
||||
getDataList,
|
||||
handleSearch,
|
||||
} = useTable(getFlashDiscountList, { pageSize: 20, sort: "createTime", order: "desc" });
|
||||
|
||||
// searchFormRef:模板 ref 改名避免与顶层变量 searchForm 互相覆盖
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
getDataList();
|
||||
});
|
||||
|
||||
function statusText(s) {
|
||||
return { NEW: "未开始", START: "进行中", END: "已结束", CLOSE: "已关闭" }[s] || s;
|
||||
}
|
||||
function statusType(s) {
|
||||
return { NEW: "info", START: "success", END: "danger", CLOSE: "danger" }[s] || "info";
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getFlashDiscountList(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function goAdd() {
|
||||
router.push({ name: "flash-discount-add" });
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<el-form-item label="分享卡片封面" prop="feedsImg">
|
||||
<div class="form-field-block">
|
||||
<div class="upload-list" v-if="liveForm.feedsImg">
|
||||
<img :src="liveForm.feedsImg">
|
||||
<img alt="直播间列表图" :src="liveForm.feedsImg">
|
||||
<div class="upload-list-cover" @click="handleView(liveForm.feedsImg)">
|
||||
<span class="view-icon">查看</span>
|
||||
</div>
|
||||
@@ -54,7 +54,7 @@
|
||||
<el-form-item label="直播间背景墙" prop="coverImg">
|
||||
<div class="form-field-block">
|
||||
<div class="upload-list" v-if="liveForm.coverImg">
|
||||
<img :src="liveForm.coverImg">
|
||||
<img alt="直播间背景图" :src="liveForm.coverImg">
|
||||
<div class="upload-list-cover" @click="handleView(liveForm.coverImg)">
|
||||
<span class="view-icon">查看</span>
|
||||
</div>
|
||||
@@ -66,7 +66,7 @@
|
||||
<el-form-item label="直播间分享图" prop="shareImg">
|
||||
<div class="form-field-block">
|
||||
<div class="upload-list" v-if="liveForm.shareImg">
|
||||
<img :src="liveForm.shareImg">
|
||||
<img alt="直播间分享图" :src="liveForm.shareImg">
|
||||
<div class="upload-list-cover" @click="handleView(liveForm.shareImg)">
|
||||
<span class="view-icon">查看</span>
|
||||
</div>
|
||||
@@ -82,7 +82,7 @@
|
||||
<template #default="{ row, $index }">
|
||||
<div v-if="row" class="flex-goods">
|
||||
<el-badge v-if="$index == 0 || $index == 1" is-dot type="danger" />
|
||||
<img class="thumbnail" :src="row.thumbnail || row.goodsImage">
|
||||
<img alt="商品图片" class="thumbnail" :src="row.thumbnail || row.goodsImage">
|
||||
{{ row.goodsName || row.name }}
|
||||
</div>
|
||||
</template>
|
||||
@@ -114,7 +114,7 @@
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="imageVisible" title="查看图片" width="600px">
|
||||
<img :src="imageSrc" v-if="imageVisible" style="width: 100%">
|
||||
<img alt="预览图片" :src="imageSrc" v-if="imageVisible" style="width: 100%">
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -55,25 +55,26 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { useRouter } from "vue-router";
|
||||
import { getNthItemDiscountList, updateNthItemDiscountStatus } from "@/api/promotion.js";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const loading = ref(false);
|
||||
const total = ref(0);
|
||||
const data = ref([]);
|
||||
const searchForm = reactive({ pageNumber: 1, pageSize: 20, sort: "createTime", order: "desc" });
|
||||
const {
|
||||
loading,
|
||||
data,
|
||||
total,
|
||||
searchForm,
|
||||
getDataList,
|
||||
handleSearch,
|
||||
} = useTable(getNthItemDiscountList, { pageSize: 20, sort: "createTime", order: "desc" });
|
||||
|
||||
// searchFormRef:模板 ref 改名避免与顶层变量 searchForm 互相覆盖
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
getDataList();
|
||||
});
|
||||
|
||||
function statusText(s) {
|
||||
return { NEW: "未开始", START: "进行中", END: "已结束", CLOSE: "已关闭" }[s] || s;
|
||||
}
|
||||
@@ -83,20 +84,6 @@ function statusType(s) {
|
||||
function discountTypeText(t) {
|
||||
return { HALF: "半价", FREE: "免单", RATE: "打折" }[t] || t;
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getNthItemDiscountList(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function goAdd() {
|
||||
router.push({ name: "nth-item-discount-add" });
|
||||
}
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
<el-button type="primary" @click="createTemp">添加页面</el-button>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="list" style="width: 100%">
|
||||
<el-table-column prop="name" label="页面名称" min-width="200" show-overflow-tooltip>
|
||||
<el-table-column prop="name" label="页面名称" min-width="180" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ row.name || "暂无模板昵称" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="180" align="center" />
|
||||
<el-table-column
|
||||
v-if="searchForm.pageType !== 'SPECIAL'"
|
||||
label="状态"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="hotzone-box">
|
||||
<div class="hotzone-item">
|
||||
<div ref="content" class="hz-m-wrap">
|
||||
<img class="hz-u-img" :src="image" />
|
||||
<img alt="热区背景图" class="hz-u-img" :src="image" />
|
||||
<ul class="hz-m-area" v-add-item>
|
||||
<zone
|
||||
class="hz-m-item"
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
@mouseup.stop
|
||||
@mousemove.stop
|
||||
>
|
||||
<img width="17" height="17" src="../assets/styles/icons8-edit-64.png" />
|
||||
<img alt="编辑" width="17" height="17" src="../assets/styles/icons8-edit-64.png" />
|
||||
</li>
|
||||
<li class="hz-flex-img">
|
||||
<img class="hz-u-img" :src="zoneForm.img" />
|
||||
<img alt="热区图片" class="hz-u-img" :src="zoneForm.img" />
|
||||
</li>
|
||||
<li class="hz-u-square hz-u-square-tl" data-pointer="dealTL"></li>
|
||||
<li class="hz-u-square hz-u-square-tc" data-pointer="dealTC"></li>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-carousel autoplay height="340px">
|
||||
<el-carousel-item v-for="(item, index) in data.options.list" :key="index">
|
||||
<div style="overflow: hidden">
|
||||
<img :src="item.img" width="790" height="340" />
|
||||
<img alt="轮播图" :src="item.img" width="790" height="340" />
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-carousel autoplay @change="autoChange">
|
||||
<el-carousel-item v-for="(item, index) in data.options.list" :key="index">
|
||||
<div style="overflow: hidden">
|
||||
<img :src="item.img" width="1200" height="470" />
|
||||
<img alt="轮播图" :src="item.img" width="1200" height="470" />
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-carousel autoplay height="340px">
|
||||
<el-carousel-item v-for="(item, index) in data.options.list" :key="index">
|
||||
<div style="overflow: hidden">
|
||||
<img :src="item.img" width="590" height="470" />
|
||||
<img alt="轮播图" :src="item.img" width="590" height="470" />
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
@@ -26,13 +26,13 @@
|
||||
:key="index"
|
||||
>
|
||||
<div class="mb_10">
|
||||
<img :src="item[0].img" width="190" height="150" />
|
||||
<img alt="轮播图" :src="item[0].img" width="190" height="150" />
|
||||
</div>
|
||||
<div class="mb_10">
|
||||
<img :src="item[1].img" width="190" height="150" />
|
||||
<img alt="轮播图" :src="item[1].img" width="190" height="150" />
|
||||
</div>
|
||||
<div>
|
||||
<img :src="item[2].img" width="190" height="150" />
|
||||
<img alt="轮播图" :src="item[2].img" width="190" height="150" />
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
class="draggable"
|
||||
>
|
||||
<div class="flex">
|
||||
<img :src="item.img" class="column-img" />
|
||||
<img alt="楼层图片" :src="item.img" class="column-img" />
|
||||
<div class="column-goods-list">
|
||||
<div><el-input v-model="item.title" /></div>
|
||||
<div><el-input v-model="item.desc" /></div>
|
||||
@@ -139,7 +139,7 @@
|
||||
class="draggable"
|
||||
>
|
||||
<div class="flex">
|
||||
<img :src="item.img" class="column-img" />
|
||||
<img alt="楼层图片" :src="item.img" class="column-img" />
|
||||
<div class="column-goods-list">
|
||||
<el-button
|
||||
size="small"
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
:key="index"
|
||||
>
|
||||
<div class="goods-img">
|
||||
<img :src="item.img" />
|
||||
<img alt="楼层图片" :src="item.img" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="goods-name wes-2">{{ item.title }}</div>
|
||||
@@ -93,7 +93,7 @@
|
||||
class="draggable"
|
||||
>
|
||||
<div class="flex">
|
||||
<img :src="item.img" class="column-img" />
|
||||
<img alt="楼层图片" :src="item.img" class="column-img" />
|
||||
<div class="flex column-goods-config">
|
||||
<div class="column-config">
|
||||
<div>
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
class="draggable"
|
||||
>
|
||||
<div class="flex">
|
||||
<img :src="item.img" class="column-img" />
|
||||
<img alt="楼层图片" :src="item.img" class="column-img" />
|
||||
<el-button
|
||||
size="small"
|
||||
class="ml_10"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="line flex flex-j-sb">
|
||||
<div class="column" v-for="(item,index) in data.options.list" :key="index">
|
||||
<div v-if="!item.img" class="placeholder">占位符</div>
|
||||
<img v-else :src="item.img" class="three-column-img">
|
||||
<img alt="楼层图片" v-else :src="item.img" class="three-column-img">
|
||||
</div>
|
||||
<div class="setup-box">
|
||||
<div>
|
||||
@@ -23,7 +23,7 @@
|
||||
append-to-body destroy-on-close>
|
||||
<div class="modal-tab-bar">
|
||||
<div v-for="(item,index) in data.options.list" :key="index">
|
||||
<img :src="item.img" class="three-column-img">
|
||||
<img alt="楼层图片" :src="item.img" class="three-column-img">
|
||||
<div>推荐尺寸:{{item.size}}</div>
|
||||
<el-button
|
||||
size="small"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="goods-desc">{{ item.desc }}</div>
|
||||
</div>
|
||||
<div class="goods-img">
|
||||
<img :src="item.img" />
|
||||
<img alt="楼层图片" :src="item.img" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="setup-box">
|
||||
@@ -47,7 +47,7 @@
|
||||
class="draggable"
|
||||
>
|
||||
<div class="flex">
|
||||
<img :src="item.img" class="column-img" />
|
||||
<img alt="楼层图片" :src="item.img" class="column-img" />
|
||||
<div class="flex column-goods-config">
|
||||
<div class="column-config">
|
||||
<div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<el-carousel class="carousel" v-if="res" autoplay :interval="5000" height="150px">
|
||||
<el-carousel-item v-for="(item, index) in res.list" :key="index">
|
||||
<div>
|
||||
<img class="image-mode" :src="item.img" />
|
||||
<img alt="轮播图" class="image-mode" :src="item.img" />
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
|
||||
@@ -9,13 +9,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
title: "五列单行图片模块",
|
||||
};
|
||||
</script>
|
||||
|
||||
<script setup>
|
||||
defineOptions({ title: "五列单行图片模块" });
|
||||
defineProps(["res"]);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -10,13 +10,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// 自定义选项(组件在 <component :is> 场景下按 title 展示名称),需保留在普通 script 块
|
||||
export default {
|
||||
title: "两张横图",
|
||||
};
|
||||
</script>
|
||||
<script setup>
|
||||
defineOptions({ title: "两张横图" });
|
||||
defineProps(["res"]);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="view-height-150">
|
||||
<img class="image-mode" :src="res.list[0].img" />
|
||||
<img alt="模板图片" class="image-mode" :src="res.list[0].img" />
|
||||
</div>
|
||||
<div class="view-height-150">
|
||||
<div class="view-height-75">
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-height-150">
|
||||
<img class="image-mode" style="height:150px;" :src="res.list[2].img" />
|
||||
<img alt="模板图片" class="image-mode" style="height:150px;" :src="res.list[2].img" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -32,13 +32,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 自定义选项(组件在 <component :is> 场景下按 title 展示名称),需保留在普通 script 块
|
||||
export default {
|
||||
title: "文字图片模板",
|
||||
};
|
||||
</script>
|
||||
<script setup>
|
||||
defineOptions({ title: "文字图片模板" });
|
||||
defineProps(["res"]);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="view-width-100">
|
||||
<img class="image-mode" :src="res.list[0].img" />
|
||||
<img alt="模板图片" class="image-mode" :src="res.list[0].img" />
|
||||
</div>
|
||||
<div class="view-width-100">
|
||||
<div class="view-height-85">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-width-100">
|
||||
<img class="image-mode" :src="res.list[2].img" />
|
||||
<img alt="模板图片" class="image-mode" :src="res.list[2].img" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
<el-button type="primary" @click="handleAdd()">添加页面</el-button>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="list" style="width: 100%">
|
||||
<el-table-column prop="name" label="页面名称" min-width="200" show-overflow-tooltip>
|
||||
<el-table-column prop="name" label="页面名称" min-width="180" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ row.name || "暂无模板昵称" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="180" align="center" />
|
||||
<el-table-column label="状态" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-switch
|
||||
@@ -30,7 +31,7 @@
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row">
|
||||
<a class="link-text" @click="handleEdit(row)">修改</a>
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
<template>
|
||||
<div class="data-report">
|
||||
<el-card>
|
||||
<h4 class="page-title">数据报表</h4>
|
||||
<p class="page-desc">选择报表模块查看本店销售数据统计与分析</p>
|
||||
<div class="module-grid">
|
||||
<div
|
||||
v-for="item in modules"
|
||||
:key="item.name"
|
||||
class="module-card"
|
||||
@click="goReport(item.name)"
|
||||
>
|
||||
<div class="report-header">
|
||||
<div>
|
||||
<h4 class="page-title">数据报表</h4>
|
||||
<p class="page-desc">选择报表模块,查看本店销售数据统计与分析</p>
|
||||
</div>
|
||||
<span class="module-count">共 {{ modules.length }} 个报表</span>
|
||||
</div>
|
||||
|
||||
<div class="module-grid">
|
||||
<div
|
||||
v-for="item in modules"
|
||||
:key="item.name"
|
||||
class="module-card"
|
||||
:style="{ '--accent': item.color, '--accent-bg': item.bg }"
|
||||
role="link"
|
||||
tabindex="0"
|
||||
@click="goReport(item.name)"
|
||||
@keyup.enter="goReport(item.name)"
|
||||
>
|
||||
<div class="module-top">
|
||||
<div class="module-icon">
|
||||
<el-icon :size="32">
|
||||
<el-icon :size="28">
|
||||
<component :is="item.icon" />
|
||||
</el-icon>
|
||||
</div>
|
||||
@@ -19,15 +29,22 @@
|
||||
<div class="module-title">{{ item.title }}</div>
|
||||
<div class="module-desc">{{ item.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="module-tags">
|
||||
<span v-for="tag in item.tags" :key="tag" class="module-tag">{{ tag }}</span>
|
||||
</div>
|
||||
|
||||
<div class="module-footer">
|
||||
<span>查看报表</span>
|
||||
<el-icon class="module-arrow"><ArrowRight /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import {
|
||||
ArrowRight,
|
||||
@@ -46,29 +63,42 @@ const REPORT_MODULES = [
|
||||
name: "storePerformanceReport",
|
||||
title: "店铺业绩报表",
|
||||
desc: "支付金额、营业额、转化率及同环比分析",
|
||||
tags: ["支付金额", "转化率", "同环比"],
|
||||
icon: TrendCharts,
|
||||
color: "#409eff",
|
||||
bg: "#ecf5ff",
|
||||
},
|
||||
{
|
||||
name: "goodsComparisonReport",
|
||||
title: "商品同比环比报表",
|
||||
desc: "商品销售金额、销量占比及同环比对比",
|
||||
tags: ["销售金额", "销量占比", "同环比"],
|
||||
icon: DataAnalysis,
|
||||
color: "#67c23a",
|
||||
bg: "#f0f9eb",
|
||||
},
|
||||
{
|
||||
name: "salesOrderDetailReport",
|
||||
title: "销售订单明细报表",
|
||||
desc: "订单流水、收款明细与优惠明细",
|
||||
tags: ["订单流水", "收款明细", "优惠明细"],
|
||||
icon: Document,
|
||||
color: "#e6a23c",
|
||||
bg: "#fdf6ec",
|
||||
},
|
||||
{
|
||||
name: "goodsSalesSummaryReport",
|
||||
title: "商品销售汇总报表",
|
||||
desc: "商品销量、净销售额及优惠汇总",
|
||||
tags: ["商品销量", "净销售额", "优惠汇总"],
|
||||
icon: Goods,
|
||||
color: "#9b6dff",
|
||||
bg: "#f4effe",
|
||||
},
|
||||
];
|
||||
|
||||
const modules = ref(REPORT_MODULES);
|
||||
// 图标组件为静态常量,不能放进 ref/reactive(否则 Vue 会警告组件被转成响应式对象)
|
||||
const modules = REPORT_MODULES;
|
||||
|
||||
function goReport(name) {
|
||||
router.push({ name });
|
||||
@@ -77,41 +107,89 @@ function goReport(name) {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.data-report {
|
||||
padding: 4px 0;
|
||||
|
||||
.report-header {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
padding: 20px 24px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
border-left: 4px solid var(--el-color-primary);
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0 0 8px;
|
||||
font-size: 18px;
|
||||
margin: 0 0 6px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.page-desc {
|
||||
margin: 0 0 20px;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.module-count {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.module-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.module-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 20px;
|
||||
padding: 24px 24px 18px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
background: #fafafa;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
outline: none;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary-light-5);
|
||||
background: #fff;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
|
||||
// 顶部强调色条
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: var(--accent);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
transform: translateY(-3px);
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
|
||||
|
||||
.module-arrow {
|
||||
transform: translateX(4px);
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.module-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.module-icon {
|
||||
@@ -121,8 +199,8 @@ function goReport(name) {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 12px;
|
||||
color: var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
color: var(--accent);
|
||||
background: var(--accent-bg);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -140,13 +218,51 @@ function goReport(name) {
|
||||
|
||||
.module-desc {
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
line-height: 1.6;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.module-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.module-tag {
|
||||
padding: 2px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
border-radius: 10px;
|
||||
color: var(--accent);
|
||||
background: var(--accent-bg);
|
||||
}
|
||||
|
||||
.module-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 12px;
|
||||
border-top: 1px dashed #ebeef5;
|
||||
font-size: 13px;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.module-arrow {
|
||||
color: #c0c4cc;
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.data-report {
|
||||
.report-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.module-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -41,11 +41,6 @@
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="品牌">
|
||||
<el-select v-model="params.brandId" clearable filterable placeholder="全部品牌" style="width: 160px">
|
||||
<el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品名称">
|
||||
<el-input v-model="params.keyword" clearable placeholder="商品名称" style="width: 160px" />
|
||||
</el-form-item>
|
||||
@@ -134,7 +129,7 @@
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import * as API_Statistics from "@/api/statistics";
|
||||
import { getBrandListData, getCategoryTree } from "@/api/goods";
|
||||
import { getCategoryTree } from "@/api/goods";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
import { unitPrice } from "@/utils/filters";
|
||||
|
||||
@@ -157,14 +152,12 @@ const params = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
categoryId: "",
|
||||
brandId: "",
|
||||
clientType: "",
|
||||
keyword: "",
|
||||
sortType: "PRICE",
|
||||
});
|
||||
const categoryList = ref([]);
|
||||
const categoryProps = ref({ value: "id", label: "name", children: "children", checkStrictly: true, emitPath: false });
|
||||
const brandList = ref([]);
|
||||
const clientTypeOptions = ref(CLIENT_TYPE_OPTIONS);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
@@ -200,11 +193,6 @@ function loadOptions() {
|
||||
if (res.success) categoryList.value = res.result || [];
|
||||
})
|
||||
.catch(() => {});
|
||||
getBrandListData()
|
||||
.then((res) => {
|
||||
if (res.success) brandList.value = res.result || [];
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
function formatDate(date, startOfDay) {
|
||||
const y = date.getFullYear();
|
||||
|
||||
@@ -76,19 +76,20 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import * as API_Statistics from "@/api/statistics";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
import * as filters from "@/utils/filters";
|
||||
|
||||
defineOptions({ name: "seller-goods-sales-summary-report" });
|
||||
|
||||
const loading = ref(false);
|
||||
const dateRange = ref([]);
|
||||
const defaultTime = ref([new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]);
|
||||
const params = reactive({ pageNumber: 1, pageSize: 20, keyword: "", sortType: "PRICE" });
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const { loading, data, total, searchForm: params, getDataList: loadData, handleSearch } = useTable(
|
||||
API_Statistics.getGoodsSalesSummaryReport,
|
||||
{ form: { keyword: "", sortType: "PRICE" }, pageSize: 20, immediate: false, beforeRequest: buildParams }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
initDefaultRange();
|
||||
@@ -109,8 +110,8 @@ function formatDate(date, startOfDay) {
|
||||
return startOfDay ? `${y}-${m}-${d} 00:00:00` : `${y}-${m}-${d} 23:59:59`;
|
||||
}
|
||||
|
||||
function buildParams() {
|
||||
const p = { ...params };
|
||||
function buildParams(base = params) {
|
||||
const p = { ...base };
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
p.startTime = dateRange.value[0];
|
||||
p.endTime = dateRange.value[1];
|
||||
@@ -118,22 +119,6 @@ function buildParams() {
|
||||
return p;
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
params.pageNumber = 1;
|
||||
loadData();
|
||||
}
|
||||
|
||||
function loadData() {
|
||||
loading.value = true;
|
||||
API_Statistics.getGoodsSalesSummaryReport(buildParams()).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
API_Statistics.exportGoodsSalesSummaryReport(buildParams()).then((blob) => {
|
||||
downloadBlob(blob, "商品销售汇总报表.xlsx");
|
||||
|
||||
@@ -68,25 +68,20 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import * as API_Statistics from "@/api/statistics";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
import { unitPrice } from "@/utils/filters";
|
||||
|
||||
defineOptions({ name: "seller-sales-order-detail-report" });
|
||||
|
||||
const loading = ref(false);
|
||||
const dateRange = ref([]);
|
||||
const defaultTime = ref([new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]);
|
||||
const params = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
orderSn: "",
|
||||
memberName: "",
|
||||
keyword: "",
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const { loading, data, total, searchForm: params, getDataList: loadData, handleSearch } = useTable(
|
||||
API_Statistics.getSalesOrderDetailReport,
|
||||
{ form: { orderSn: "", memberName: "", keyword: "" }, pageSize: 20, immediate: false, beforeRequest: buildParams }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
initDefaultRange();
|
||||
@@ -105,28 +100,14 @@ function formatDate(date, startOfDay) {
|
||||
const d = String(date.getDate()).padStart(2, "0");
|
||||
return startOfDay ? `${y}-${m}-${d} 00:00:00` : `${y}-${m}-${d} 23:59:59`;
|
||||
}
|
||||
function buildParams() {
|
||||
const p = { ...params };
|
||||
function buildParams(base = params) {
|
||||
const p = { ...base };
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
p.startTime = dateRange.value[0];
|
||||
p.endTime = dateRange.value[1];
|
||||
}
|
||||
return p;
|
||||
}
|
||||
function handleSearch() {
|
||||
params.pageNumber = 1;
|
||||
loadData();
|
||||
}
|
||||
function loadData() {
|
||||
loading.value = true;
|
||||
API_Statistics.getSalesOrderDetailReport(buildParams()).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleExport() {
|
||||
API_Statistics.exportSalesOrderDetailReport(buildParams()).then((blob) => {
|
||||
downloadBlob(blob, "销售订单明细报表.xlsx");
|
||||
|
||||
@@ -86,19 +86,20 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import * as API_Statistics from "@/api/statistics";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
import { unitPrice } from "@/utils/filters";
|
||||
|
||||
defineOptions({ name: "seller-store-performance-report" });
|
||||
|
||||
const loading = ref(false);
|
||||
const dateRange = ref([]);
|
||||
const defaultTime = ref([new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]);
|
||||
const params = reactive({ pageNumber: 1, pageSize: 20 });
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const { loading, data, total, searchForm: params, getDataList: loadData, handleSearch } = useTable(
|
||||
API_Statistics.getStorePerformanceReport,
|
||||
{ pageSize: 20, immediate: false, beforeRequest: buildParams }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
initDefaultRange();
|
||||
@@ -117,28 +118,14 @@ function formatDate(date, startOfDay) {
|
||||
const d = String(date.getDate()).padStart(2, "0");
|
||||
return startOfDay ? `${y}-${m}-${d} 00:00:00` : `${y}-${m}-${d} 23:59:59`;
|
||||
}
|
||||
function buildParams() {
|
||||
const p = { ...params };
|
||||
function buildParams(base = params) {
|
||||
const p = { ...base };
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
p.startTime = dateRange.value[0];
|
||||
p.endTime = dateRange.value[1];
|
||||
}
|
||||
return p;
|
||||
}
|
||||
function handleSearch() {
|
||||
params.pageNumber = 1;
|
||||
loadData();
|
||||
}
|
||||
function loadData() {
|
||||
loading.value = true;
|
||||
API_Statistics.getStorePerformanceReport(buildParams()).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleExport() {
|
||||
API_Statistics.exportStorePerformanceReport(buildParams()).then((blob) => {
|
||||
downloadBlob(blob, "店铺业绩报表.xlsx");
|
||||
|
||||
Reference in New Issue
Block a user