|
|
@@ -0,0 +1,829 @@
|
|
|
+<template>
|
|
|
+ <div class="facility-management-container">
|
|
|
+ <!-- 标签页 -->
|
|
|
+ <div class="header-section">
|
|
|
+ <el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
|
|
+ <el-tab-pane v-for="tab in tabs" :key="tab.value" :label="tab.label" :name="tab.value" />
|
|
|
+ </el-tabs>
|
|
|
+ <div class="action-buttons">
|
|
|
+ <el-button type="primary" @click="openCreateDialog"> 添加 </el-button>
|
|
|
+ <el-button type="primary" @click="handleBatchImport"> 批量导入 </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 添加实景图片区域 -->
|
|
|
+ <div class="image-upload-section">
|
|
|
+ <div class="section-title">添加实景图片</div>
|
|
|
+ <div class="upload-area">
|
|
|
+ <el-upload
|
|
|
+ v-model:file-list="imageFileList"
|
|
|
+ list-type="picture-card"
|
|
|
+ :limit="20"
|
|
|
+ :on-preview="handlePictureCardPreview"
|
|
|
+ :on-remove="handleRemoveImage"
|
|
|
+ :before-upload="beforeImageUpload"
|
|
|
+ :http-request="handleImageUpload"
|
|
|
+ accept="image/*"
|
|
|
+ >
|
|
|
+ <el-icon><Plus /></el-icon>
|
|
|
+ <div class="upload-tip">({{ imageFileList.length }}/20)</div>
|
|
|
+ </el-upload>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 设施列表 -->
|
|
|
+ <div class="facility-list-section">
|
|
|
+ <div class="section-title">设施列表</div>
|
|
|
+ <el-table :data="paginatedList" border style="width: 100%">
|
|
|
+ <el-table-column type="index" label="序号" width="60" align="center" />
|
|
|
+ <el-table-column prop="facilityName" label="名称" min-width="120" />
|
|
|
+ <el-table-column prop="quantity" label="数量" width="100" align="center" />
|
|
|
+ <el-table-column prop="brand" label="品牌" min-width="120" />
|
|
|
+ <el-table-column prop="displayInStoreDetail" label="展示在店铺详情" width="150" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="row.displayInStoreDetail === 1 ? 'success' : 'info'">
|
|
|
+ {{ row.displayInStoreDetail === 1 ? "显示" : "隐藏" }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="200" align="center" fixed="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button type="primary" link @click="editItem(row)"> 编辑 </el-button>
|
|
|
+ <el-button type="primary" link @click="deleteItem(row)"> 删除 </el-button>
|
|
|
+ <el-button type="primary" link @click="toggleDisplay(row)">
|
|
|
+ {{ row.displayInStoreDetail === 1 ? "隐藏" : "显示" }}
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 分页 -->
|
|
|
+ <div class="pagination-section">
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="pageable.pageNum"
|
|
|
+ v-model:page-size="pageable.pageSize"
|
|
|
+ :page-sizes="[10, 20, 50, 100]"
|
|
|
+ :total="pageable.total"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ @current-change="handleCurrentChange"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 新建/编辑弹窗 -->
|
|
|
+ <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px" @close="resetForm">
|
|
|
+ <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">
|
|
|
+ <el-form-item label="选择分类*" prop="facilityCategory">
|
|
|
+ <el-select v-model="formData.facilityCategory" placeholder="请选择" style="width: 100%">
|
|
|
+ <el-option v-for="tab in tabs" :key="tab.value" :label="tab.label" :value="tab.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="名称*" prop="facilityName">
|
|
|
+ <el-input v-model="formData.facilityName" placeholder="请输入" maxlength="50" clearable />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="数量" prop="quantity">
|
|
|
+ <el-input-number v-model="formData.quantity" :min="0" :max="9999" placeholder="请输入" style="width: 100%" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="品牌" prop="brand">
|
|
|
+ <el-input v-model="formData.brand" placeholder="请输入" maxlength="50" clearable />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="描述" prop="description">
|
|
|
+ <el-input
|
|
|
+ v-model="formData.description"
|
|
|
+ type="textarea"
|
|
|
+ :rows="4"
|
|
|
+ placeholder="请输入"
|
|
|
+ maxlength="500"
|
|
|
+ show-word-limit
|
|
|
+ clearable
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="展示在店铺详情" prop="displayInStoreDetail">
|
|
|
+ <el-radio-group v-model="formData.displayInStoreDetail">
|
|
|
+ <el-radio :label="1"> 显示 </el-radio>
|
|
|
+ <el-radio :label="0"> 隐藏 </el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="dialogVisible = false"> 取消 </el-button>
|
|
|
+ <el-button type="primary" :loading="submitLoading" @click="handleSubmit">
|
|
|
+ {{ editId ? "确定" : "添加" }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 批量导入弹窗 -->
|
|
|
+ <el-dialog v-model="batchImportVisible" title="批量导入" width="500px">
|
|
|
+ <div class="import-steps">
|
|
|
+ <div class="import-step">
|
|
|
+ <div class="step-header">
|
|
|
+ <div class="step-number">1</div>
|
|
|
+ <div class="step-title">下载模板</div>
|
|
|
+ </div>
|
|
|
+ <el-button type="primary" @click="downloadTemplate"> 下载excel模板 </el-button>
|
|
|
+ </div>
|
|
|
+ <div class="import-step">
|
|
|
+ <div class="step-header">
|
|
|
+ <div class="step-number">2</div>
|
|
|
+ <div class="step-title">上传文件</div>
|
|
|
+ </div>
|
|
|
+ <el-upload
|
|
|
+ ref="uploadRef"
|
|
|
+ :auto-upload="false"
|
|
|
+ :on-change="handleFileChange"
|
|
|
+ :on-remove="handleFileRemove"
|
|
|
+ :limit="1"
|
|
|
+ accept=".xlsx,.xls"
|
|
|
+ drag
|
|
|
+ class="import-upload"
|
|
|
+ >
|
|
|
+ <el-icon class="el-icon--upload">
|
|
|
+ <UploadFilled />
|
|
|
+ </el-icon>
|
|
|
+ <div class="el-upload__text">点击上传文件或拖拽上传文件</div>
|
|
|
+ </el-upload>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="batchImportVisible = false"> 取消 </el-button>
|
|
|
+ <el-button type="primary" :loading="importLoading" @click="handleImportSubmit"> 导入 </el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 图片预览 -->
|
|
|
+ <el-image-viewer
|
|
|
+ v-if="imageViewerVisible"
|
|
|
+ :url-list="imageViewerUrlList"
|
|
|
+ :initial-index="imageViewerInitialIndex"
|
|
|
+ @close="imageViewerVisible = false"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, reactive, computed, onMounted } from "vue";
|
|
|
+import { ElMessage, ElMessageBox } from "element-plus";
|
|
|
+import type { FormInstance, FormRules, UploadFile, UploadRequestOptions } from "element-plus";
|
|
|
+import { Plus, UploadFilled } from "@element-plus/icons-vue";
|
|
|
+import { uploadImg } from "@/api/modules/newLoginApi";
|
|
|
+import { localGet } from "@/utils";
|
|
|
+import {
|
|
|
+ getFacilityList,
|
|
|
+ createOrUpdateFacility,
|
|
|
+ getFacilityDetail,
|
|
|
+ deleteFacility,
|
|
|
+ downloadFacilityTemplate,
|
|
|
+ importFacilityExcel,
|
|
|
+ getOfficialImgList,
|
|
|
+ saveOfficialImg
|
|
|
+} from "@/api/modules/storeDecoration";
|
|
|
+
|
|
|
+// 设施接口
|
|
|
+interface Facility {
|
|
|
+ id?: string | number;
|
|
|
+ facilityCategory: number; // 1:有氧区, 2:力量区, 3:单功能机械区
|
|
|
+ facilityName: string;
|
|
|
+ quantity?: number;
|
|
|
+ brand?: string;
|
|
|
+ description?: string;
|
|
|
+ displayInStoreDetail: number; // 0:隐藏, 1:显示
|
|
|
+}
|
|
|
+
|
|
|
+const dialogVisible = ref(false);
|
|
|
+const formRef = ref<FormInstance>();
|
|
|
+const submitLoading = ref(false);
|
|
|
+const activeTab = ref(1); // 有氧区
|
|
|
+const editId = ref<string | number | null>(null);
|
|
|
+const batchImportVisible = ref(false);
|
|
|
+const importLoading = ref(false);
|
|
|
+const uploadRef = ref();
|
|
|
+const importFile = ref<UploadFile | null>(null);
|
|
|
+const imageFileList = ref<any[]>([]);
|
|
|
+const imageViewerVisible = ref(false);
|
|
|
+const imageViewerUrlList = ref<string[]>([]);
|
|
|
+const imageViewerInitialIndex = ref(0);
|
|
|
+
|
|
|
+// 标签页配置
|
|
|
+const tabs = [
|
|
|
+ { label: "有氧区", value: 1 },
|
|
|
+ { label: "力量区", value: 2 },
|
|
|
+ { label: "单功能机械区", value: 3 }
|
|
|
+];
|
|
|
+
|
|
|
+// 设施列表
|
|
|
+const facilityList = ref<Facility[]>([]);
|
|
|
+
|
|
|
+// 分页数据
|
|
|
+const pageable = reactive({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ total: 0
|
|
|
+});
|
|
|
+
|
|
|
+// 分页后的列表
|
|
|
+const paginatedList = computed(() => {
|
|
|
+ const list = facilityList.value.filter(item => item.facilityCategory === activeTab.value);
|
|
|
+ const start = (pageable.pageNum - 1) * pageable.pageSize;
|
|
|
+ const end = start + pageable.pageSize;
|
|
|
+ return list.slice(start, end);
|
|
|
+});
|
|
|
+
|
|
|
+// 弹窗标题
|
|
|
+const dialogTitle = computed(() => (editId.value !== null ? "编辑" : "添加"));
|
|
|
+
|
|
|
+// 表单数据
|
|
|
+const formData = reactive<Facility>({
|
|
|
+ facilityCategory: 1,
|
|
|
+ facilityName: "",
|
|
|
+ quantity: undefined,
|
|
|
+ brand: "",
|
|
|
+ description: "",
|
|
|
+ displayInStoreDetail: 1
|
|
|
+});
|
|
|
+
|
|
|
+// 表单校验规则
|
|
|
+const rules = reactive<FormRules>({
|
|
|
+ facilityCategory: [{ required: true, message: "请选择分类", trigger: "change" }],
|
|
|
+ facilityName: [{ required: true, message: "请输入名称", trigger: "blur" }]
|
|
|
+});
|
|
|
+
|
|
|
+// Tab切换
|
|
|
+const handleTabClick = async () => {
|
|
|
+ pageable.pageNum = 1;
|
|
|
+ // 调用loadFacilityList获取当前选中分类的数据
|
|
|
+ await loadFacilityList(activeTab.value);
|
|
|
+ updatePagination();
|
|
|
+ // 重新获取当前分区的图片列表
|
|
|
+ getImageList();
|
|
|
+};
|
|
|
+
|
|
|
+// 更新分页总数
|
|
|
+const updatePagination = () => {
|
|
|
+ const list = facilityList.value.filter(item => item.facilityCategory === activeTab.value);
|
|
|
+ pageable.total = list.length;
|
|
|
+};
|
|
|
+
|
|
|
+// 分页大小改变
|
|
|
+const handleSizeChange = (size: number) => {
|
|
|
+ pageable.pageSize = size;
|
|
|
+ pageable.pageNum = 1;
|
|
|
+ updatePagination();
|
|
|
+};
|
|
|
+
|
|
|
+// 当前页改变
|
|
|
+const handleCurrentChange = (page: number) => {
|
|
|
+ pageable.pageNum = page;
|
|
|
+};
|
|
|
+
|
|
|
+// 打开新建弹窗
|
|
|
+const openCreateDialog = () => {
|
|
|
+ editId.value = null;
|
|
|
+ resetForm();
|
|
|
+ dialogVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+// 编辑
|
|
|
+const editItem = async (item: Facility) => {
|
|
|
+ if (!item.id) {
|
|
|
+ ElMessage.warning("设施ID不存在");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res: any = await getFacilityDetail({ id: item.id });
|
|
|
+ if (res && (res.code === 200 || res.code === "200") && res.data) {
|
|
|
+ const detail = res.data;
|
|
|
+ editId.value = detail.id;
|
|
|
+ formData.facilityCategory = detail.facilityCategory || 1;
|
|
|
+ formData.facilityName = detail.facilityName || "";
|
|
|
+ // 新API中没有这些字段,设置为默认值
|
|
|
+ formData.quantity = undefined;
|
|
|
+ formData.brand = "";
|
|
|
+ formData.description = "";
|
|
|
+ formData.displayInStoreDetail = detail.displayInStoreDetail !== undefined ? detail.displayInStoreDetail : 1;
|
|
|
+ dialogVisible.value = true;
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res?.msg || "获取设施详情失败");
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error("获取设施详情失败:", error);
|
|
|
+ ElMessage.error(error?.msg || "获取设施详情失败,请重试");
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 删除
|
|
|
+const deleteItem = async (item: Facility) => {
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm("确认删除该设施吗?", "提示", {
|
|
|
+ confirmButtonText: "确定",
|
|
|
+ cancelButtonText: "取消",
|
|
|
+ type: "warning"
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!item.id) {
|
|
|
+ ElMessage.error("设施ID不存在");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const res: any = await deleteFacility({ id: item.id });
|
|
|
+ if (res && (res.code === 200 || res.code === "200")) {
|
|
|
+ ElMessage.success("删除成功");
|
|
|
+ await loadFacilityList();
|
|
|
+ updatePagination();
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res?.msg || "删除失败");
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ if (error !== "cancel") {
|
|
|
+ console.error("删除设施失败:", error);
|
|
|
+ ElMessage.error(error?.msg || "删除失败,请重试");
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 切换显示状态
|
|
|
+const toggleDisplay = async (item: Facility) => {
|
|
|
+ try {
|
|
|
+ if (!item.id) {
|
|
|
+ ElMessage.error("设施ID不存在");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const newStatus = item.displayInStoreDetail === 1 ? 0 : 1;
|
|
|
+ const res: any = await createOrUpdateFacility({
|
|
|
+ ...item,
|
|
|
+ displayInStoreDetail: newStatus
|
|
|
+ });
|
|
|
+ if (res && (res.code === 200 || res.code === "200")) {
|
|
|
+ ElMessage.success(newStatus === 1 ? "已显示" : "已隐藏");
|
|
|
+ await loadFacilityList();
|
|
|
+ updatePagination();
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res?.msg || "操作失败");
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error("切换状态失败:", error);
|
|
|
+ ElMessage.error(error?.msg || "操作失败,请重试");
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 图片上传前验证
|
|
|
+const beforeImageUpload = (file: File) => {
|
|
|
+ const isImage = file.type.startsWith("image/");
|
|
|
+ const isLt10M = file.size / 1024 / 1024 < 10;
|
|
|
+
|
|
|
+ if (!isImage) {
|
|
|
+ ElMessage.error("只能上传图片文件!");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!isLt10M) {
|
|
|
+ ElMessage.error("图片大小不能超过10MB!");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (imageFileList.value.length >= 20) {
|
|
|
+ ElMessage.error("最多只能上传20张图片!");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+};
|
|
|
+
|
|
|
+// 图片上传
|
|
|
+const handleImageUpload = async (options: UploadRequestOptions) => {
|
|
|
+ try {
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append("file", options.file);
|
|
|
+ const res: any = await uploadImg(formData);
|
|
|
+ if (res && res.code === 200 && res.data) {
|
|
|
+ const imageUrl = Array.isArray(res.data) ? res.data[0] : res.data;
|
|
|
+ options.onSuccess({ fileUrl: imageUrl });
|
|
|
+
|
|
|
+ // 调用saveOfficialImg接口保存图片信息
|
|
|
+ const userInfo: any = localGet("geeker-user")?.userInfo || {};
|
|
|
+ const storeId = userInfo.storeId;
|
|
|
+ if (storeId) {
|
|
|
+ const saveRes: any = await saveOfficialImg([
|
|
|
+ {
|
|
|
+ imgUrl: imageUrl,
|
|
|
+ imgType: 28,
|
|
|
+ businessId: activeTab.value, // 1:有氧区, 2:力量区, 3:单功能机械区
|
|
|
+ storeId: Number(storeId),
|
|
|
+ imgSort: 0 // 增加排序参数,默认值为0
|
|
|
+ }
|
|
|
+ ]);
|
|
|
+ if (!(saveRes && (saveRes.code === 200 || saveRes.code === "200"))) {
|
|
|
+ console.error("保存图片信息失败:", saveRes);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ElMessage.success("图片上传成功");
|
|
|
+ } else {
|
|
|
+ options.onError(new Error(res?.msg || "图片上传失败"));
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ options.onError(error);
|
|
|
+ ElMessage.error(error?.msg || "图片上传失败");
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 图片预览
|
|
|
+const handlePictureCardPreview = (file: any) => {
|
|
|
+ const url = file.url || file.response?.fileUrl;
|
|
|
+ if (url) {
|
|
|
+ imageViewerUrlList.value = imageFileList.value.map((item: any) => item.url || item.response?.fileUrl).filter(Boolean);
|
|
|
+ imageViewerInitialIndex.value = imageViewerUrlList.value.indexOf(url);
|
|
|
+ imageViewerVisible.value = true;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 删除图片
|
|
|
+const handleRemoveImage = async (file: any) => {
|
|
|
+ const index = imageFileList.value.findIndex(item => item.uid === file.uid);
|
|
|
+ if (index > -1) {
|
|
|
+ try {
|
|
|
+ const userInfo: any = localGet("geeker-user")?.userInfo || {};
|
|
|
+ const storeId = userInfo.storeId;
|
|
|
+ if (storeId) {
|
|
|
+ // 调用删除图片的API,这里假设API为deleteOfficialImg
|
|
|
+ // 注意:实际使用时需要替换为真实的API
|
|
|
+ // await deleteOfficialImg({ id: file.uid, storeId: Number(storeId) });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("删除图片失败:", error);
|
|
|
+ ElMessage.error("删除图片失败,请重试");
|
|
|
+ }
|
|
|
+ imageFileList.value.splice(index, 1);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 重置表单
|
|
|
+const resetForm = () => {
|
|
|
+ formData.facilityCategory = activeTab.value;
|
|
|
+ formData.facilityName = "";
|
|
|
+ formData.quantity = undefined;
|
|
|
+ formData.brand = "";
|
|
|
+ formData.description = "";
|
|
|
+ formData.displayInStoreDetail = 1;
|
|
|
+ editId.value = null;
|
|
|
+ formRef.value?.clearValidate();
|
|
|
+};
|
|
|
+
|
|
|
+// 提交表单
|
|
|
+const handleSubmit = async () => {
|
|
|
+ if (!formRef.value) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ await formRef.value.validate();
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.warning("请完善必填项");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ submitLoading.value = true;
|
|
|
+ try {
|
|
|
+ const userInfo: any = localGet("geeker-user")?.userInfo || {};
|
|
|
+ const storeId = userInfo.storeId;
|
|
|
+ if (!storeId) {
|
|
|
+ ElMessage.error("未找到店铺ID");
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const params: any = {
|
|
|
+ storeId: Number(storeId),
|
|
|
+ facilityCategory: formData.facilityCategory,
|
|
|
+ facilityName: formData.facilityName,
|
|
|
+ quantity: formData.quantity,
|
|
|
+ brand: formData.brand || "",
|
|
|
+ description: formData.description || "",
|
|
|
+ displayInStoreDetail: formData.displayInStoreDetail
|
|
|
+ };
|
|
|
+
|
|
|
+ if (editId.value) {
|
|
|
+ params.id = editId.value;
|
|
|
+ }
|
|
|
+
|
|
|
+ const res: any = await createOrUpdateFacility(params);
|
|
|
+ if (res && (res.code === 200 || res.code === "200")) {
|
|
|
+ // 获取保存后的设施ID
|
|
|
+ const facilityId = res.data?.id || (editId.value ? editId.value : null);
|
|
|
+
|
|
|
+ // 如果有图片列表,调用saveOfficialImg接口上传文件信息
|
|
|
+ if (facilityId && imageFileList.value.length > 0) {
|
|
|
+ const userInfo: any = localGet("geeker-user")?.userInfo || {};
|
|
|
+ const storeId = userInfo.storeId;
|
|
|
+ if (storeId) {
|
|
|
+ // 准备图片数据
|
|
|
+ const imageData = imageFileList.value.map((file: any, index: number) => ({
|
|
|
+ imgUrl: file.url || file.response?.fileUrl,
|
|
|
+ imgType: 28, // 设施图片类型
|
|
|
+ businessId: facilityId, // 设施ID
|
|
|
+ storeId: Number(storeId),
|
|
|
+ imgSort: index // 图片排序
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 调用saveOfficialImg接口
|
|
|
+ const saveRes: any = await saveOfficialImg(imageData);
|
|
|
+ if (!(saveRes && (saveRes.code === 200 || saveRes.code === "200"))) {
|
|
|
+ console.error("保存图片信息失败:", saveRes);
|
|
|
+ ElMessage.warning("图片信息保存失败,请稍后重试");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ElMessage.success(editId.value ? "编辑成功" : "添加成功");
|
|
|
+ dialogVisible.value = false;
|
|
|
+ resetForm();
|
|
|
+ await loadFacilityList();
|
|
|
+ updatePagination();
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res?.msg || (editId.value ? "编辑失败" : "添加失败"));
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error("操作失败:", error);
|
|
|
+ ElMessage.error(error?.msg || "操作失败,请重试");
|
|
|
+ } finally {
|
|
|
+ submitLoading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 加载设施列表
|
|
|
+const loadFacilityList = async (category?: number) => {
|
|
|
+ try {
|
|
|
+ const userInfo: any = localGet("geeker-user")?.userInfo || {};
|
|
|
+ const storeId = userInfo.storeId;
|
|
|
+ if (!storeId) {
|
|
|
+ console.warn("未找到店铺ID");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果传入了分类则使用传入的分类,否则使用当前选中的标签页分类
|
|
|
+ const facilityCategory = category !== undefined ? category : activeTab.value;
|
|
|
+
|
|
|
+ const res: any = await getFacilityList({
|
|
|
+ storeId: Number(storeId),
|
|
|
+ facilityCategory: facilityCategory
|
|
|
+ });
|
|
|
+ if (res && (res.code === 200 || res.code === "200") && res.data) {
|
|
|
+ const dataList = Array.isArray(res.data) ? res.data : [];
|
|
|
+
|
|
|
+ // 如果是加载指定分类的数据,则只更新该分类的数据
|
|
|
+ if (category !== undefined) {
|
|
|
+ // 先移除该分类的旧数据
|
|
|
+ facilityList.value = facilityList.value.filter(item => item.facilityCategory !== category);
|
|
|
+ // 再添加新数据
|
|
|
+ facilityList.value.push(
|
|
|
+ ...dataList.map((item: any) => ({
|
|
|
+ id: item.id,
|
|
|
+ facilityCategory: item.facilityCategory || category,
|
|
|
+ facilityName: item.facilityName || "",
|
|
|
+ quantity: item.quantity,
|
|
|
+ brand: item.brand || "",
|
|
|
+ description: item.description || "",
|
|
|
+ displayInStoreDetail: item.displayInStoreDetail !== undefined ? item.displayInStoreDetail : 1
|
|
|
+ }))
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ // 如果是加载所有数据,则直接替换
|
|
|
+ facilityList.value = dataList.map((item: any) => ({
|
|
|
+ id: item.id,
|
|
|
+ facilityCategory: item.facilityCategory || 1,
|
|
|
+ facilityName: item.facilityName || "",
|
|
|
+ quantity: item.quantity,
|
|
|
+ brand: item.brand || "",
|
|
|
+ description: item.description || "",
|
|
|
+ displayInStoreDetail: item.displayInStoreDetail !== undefined ? item.displayInStoreDetail : 1
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("获取设施列表失败:", error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 批量导入
|
|
|
+const handleBatchImport = () => {
|
|
|
+ batchImportVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+// 文件选择
|
|
|
+const handleFileChange = (file: UploadFile) => {
|
|
|
+ importFile.value = file;
|
|
|
+};
|
|
|
+
|
|
|
+// 文件移除
|
|
|
+const handleFileRemove = () => {
|
|
|
+ importFile.value = null;
|
|
|
+};
|
|
|
+
|
|
|
+// 下载模板
|
|
|
+const downloadTemplate = async () => {
|
|
|
+ try {
|
|
|
+ const res: any = await downloadFacilityTemplate();
|
|
|
+ const blob =
|
|
|
+ res instanceof Blob
|
|
|
+ ? res
|
|
|
+ : new Blob([res], {
|
|
|
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
|
+ });
|
|
|
+ const url = window.URL.createObjectURL(blob);
|
|
|
+ const link = document.createElement("a");
|
|
|
+ link.href = url;
|
|
|
+ link.download = `设施导入模板_${new Date().getTime()}.xlsx`;
|
|
|
+ document.body.appendChild(link);
|
|
|
+ link.click();
|
|
|
+ document.body.removeChild(link);
|
|
|
+ window.URL.revokeObjectURL(url);
|
|
|
+ ElMessage.success("模板下载成功");
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error("下载模板失败:", error);
|
|
|
+ ElMessage.error(error?.msg || "模板下载失败,请重试");
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 提交导入
|
|
|
+const handleImportSubmit = async () => {
|
|
|
+ if (!importFile.value || !importFile.value.raw) {
|
|
|
+ ElMessage.warning("请先选择文件");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const userInfo: any = localGet("geeker-user")?.userInfo || {};
|
|
|
+ const storeId = userInfo.storeId;
|
|
|
+ if (!storeId) {
|
|
|
+ ElMessage.error("未找到店铺ID");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ importLoading.value = true;
|
|
|
+ try {
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append("file", importFile.value.raw);
|
|
|
+ const res: any = await importFacilityExcel(formData, storeId);
|
|
|
+ if (res && (res.code === 200 || res.code === "200")) {
|
|
|
+ ElMessage.success("导入成功");
|
|
|
+ batchImportVisible.value = false;
|
|
|
+ importFile.value = null;
|
|
|
+ if (uploadRef.value) {
|
|
|
+ uploadRef.value.clearFiles();
|
|
|
+ }
|
|
|
+ await loadFacilityList();
|
|
|
+ updatePagination();
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res?.msg || "导入失败");
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error("导入失败:", error);
|
|
|
+ ElMessage.error(error?.msg || "导入失败,请重试");
|
|
|
+ } finally {
|
|
|
+ importLoading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 获取图片列表
|
|
|
+const getImageList = async () => {
|
|
|
+ try {
|
|
|
+ const userInfo: any = localGet("geeker-user")?.userInfo || {};
|
|
|
+ const storeId = userInfo.storeId;
|
|
|
+ if (storeId) {
|
|
|
+ const res: any = await getOfficialImgList(activeTab.value, Number(storeId), 28);
|
|
|
+ if (res && res.code === 200 && Array.isArray(res.data)) {
|
|
|
+ // 将返回的图片数据转换为el-upload组件所需的格式
|
|
|
+ imageFileList.value = res.data.map(item => ({
|
|
|
+ name: item.imgUrl?.split("/").pop() || "",
|
|
|
+ url: item.imgUrl,
|
|
|
+ uid: item.id || Math.random().toString(36).substr(2, 9)
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("获取图片列表失败:", error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 页面初始化
|
|
|
+onMounted(async () => {
|
|
|
+ await loadFacilityList();
|
|
|
+ updatePagination();
|
|
|
+ getImageList();
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.facility-management-container {
|
|
|
+ .header-section {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ :deep(.el-tabs) {
|
|
|
+ flex: 1;
|
|
|
+ .el-tabs__header {
|
|
|
+ margin: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .action-buttons {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .image-upload-section {
|
|
|
+ padding: 20px;
|
|
|
+ margin-bottom: 30px;
|
|
|
+ background-color: var(--el-bg-color-page);
|
|
|
+ border-radius: 8px;
|
|
|
+ .section-title {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ }
|
|
|
+ .upload-area {
|
|
|
+ :deep(.el-upload--picture-card) {
|
|
|
+ position: relative;
|
|
|
+ .upload-tip {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 8px;
|
|
|
+ left: 50%;
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
+ transform: translateX(-50%);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .facility-list-section {
|
|
|
+ .section-title {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ }
|
|
|
+ .pagination-section {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .dialog-footer {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ justify-content: flex-end;
|
|
|
+ }
|
|
|
+ .import-steps {
|
|
|
+ .import-step {
|
|
|
+ margin-bottom: 30px;
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+ .step-header {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ .step-number {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: white;
|
|
|
+ background-color: var(--el-color-primary);
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
+ .step-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .import-upload {
|
|
|
+ :deep(.el-upload-dragger) {
|
|
|
+ width: 100%;
|
|
|
+ padding: 40px 20px;
|
|
|
+ .el-icon--upload {
|
|
|
+ font-size: 48px;
|
|
|
+ color: var(--el-text-color-placeholder);
|
|
|
+ }
|
|
|
+ .el-upload__text {
|
|
|
+ margin-top: 16px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
+ em {
|
|
|
+ font-style: normal;
|
|
|
+ color: var(--el-color-primary);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|