| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- <script setup lang="ts">
- import { ref, reactive, onMounted } from "vue";
- import { ElMessage, ElMessageBox } from "element-plus";
- import { useRenderIcon } from "@/components/ReIcon/src/hooks";
- import Refresh from "~icons/ep/refresh";
- import Link from "~icons/ep/link";
- import Remove from "~icons/ep/remove";
- import Search from "~icons/ep/search";
- import {
- syncErpShops,
- getErpShopList,
- bindErpShop,
- unbindErpShop,
- autoBindErpShop,
- unbindAllErpShop
- } from "@/api/erpShop";
- import { getDeviceList } from "@/api/device";
- defineOptions({
- name: "ErpShopList"
- });
- const loading = ref(false);
- const syncing = ref(false);
- const autoBinding = ref(false);
- const unbindingAll = ref(false);
- const form = reactive({
- keyword: "",
- boundStatus: undefined as number | undefined
- });
- const dataList = ref<any[]>([]);
- const total = ref(0);
- const pagination = reactive({ currentPage: 1, pageSize: 10 });
- const deviceOptions = ref<any[]>([]);
- const bindDialogVisible = ref(false);
- const currentErpShop = ref<any>(null);
- const selectedDeviceId = ref<number | null>(null);
- const bindLoading = ref(false);
- async function fetchList() {
- loading.value = true;
- try {
- const res = await getErpShopList({
- keyword: form.keyword || undefined,
- boundStatus: form.boundStatus,
- page: pagination.currentPage,
- pageSize: pagination.pageSize
- });
- if (res.code === 200) {
- dataList.value = res.data?.list || [];
- total.value = res.data?.total || 0;
- await fetchBoundStatus();
- }
- } finally {
- loading.value = false;
- }
- }
- function resetForm() {
- form.keyword = "";
- form.boundStatus = undefined;
- pagination.currentPage = 1;
- fetchList();
- }
- function handleSearch() {
- pagination.currentPage = 1;
- fetchList();
- }
- function handlePageChange(page: number) {
- pagination.currentPage = page;
- fetchList();
- }
- function handleSizeChange(size: number) {
- pagination.currentPage = 1;
- pagination.pageSize = size;
- fetchList();
- }
- async function fetchBoundStatus() {
- try {
- const res = await getDeviceList({ page: 1, pageSize: 1000 });
- if (res.code === 200) {
- const devices = res.data?.list || res.data || [];
- dataList.value.forEach((erp: any) => {
- const bound = devices.find((d: any) => d.erpShopId === erp.erpShopId);
- erp._boundDevice = bound || null;
- });
- }
- } catch {
- // ignore
- }
- }
- async function handleSync() {
- syncing.value = true;
- try {
- const res = await syncErpShops();
- if (res.code === 200) {
- ElMessage.success(res.message || "同步任务已启动");
- } else {
- ElMessage.error(res.message || "同步失败");
- }
- } catch {
- ElMessage.error("同步请求失败,请稍后重试");
- } finally {
- syncing.value = false;
- }
- }
- async function handleAutoBind() {
- autoBinding.value = true;
- try {
- const res = await autoBindErpShop();
- if (res.code === 200) {
- ElMessage.success(res.message || "自动绑定完成");
- await fetchList();
- } else {
- ElMessage.error(res.message || "自动绑定失败");
- }
- } catch {
- ElMessage.error("自动绑定请求失败");
- } finally {
- autoBinding.value = false;
- }
- }
- async function handleUnbindAll() {
- try {
- await ElMessageBox.confirm(
- "确认解除所有设备与 ERP 店铺的绑定?此操作不可恢复。",
- "一键解绑",
- { type: "error", confirmButtonText: "确认解绑", cancelButtonText: "取消" }
- );
- } catch { return; }
- unbindingAll.value = true;
- try {
- const res = await unbindAllErpShop();
- if (res.code === 200) {
- ElMessage.success(res.message || `已解绑 ${res.data} 台设备`);
- await fetchList();
- } else {
- ElMessage.error(res.message || "解绑失败");
- }
- } catch {
- ElMessage.error("解绑请求失败");
- } finally {
- unbindingAll.value = false;
- }
- }
- function openBindDialog(row: any) {
- currentErpShop.value = row;
- selectedDeviceId.value = row._boundDevice?.id || null;
- bindDialogVisible.value = true;
- getDeviceList({ page: 1, pageSize: 1000 }).then(res => {
- if (res.code === 200) {
- deviceOptions.value = res.data?.list || res.data || [];
- }
- });
- }
- async function handleBind() {
- if (!selectedDeviceId.value) {
- ElMessage.warning("请选择设备");
- return;
- }
- bindLoading.value = true;
- try {
- const res = await bindErpShop(selectedDeviceId.value, currentErpShop.value.erpShopId);
- if (res.code === 200) {
- ElMessage.success("绑定成功");
- bindDialogVisible.value = false;
- await fetchList();
- } else {
- ElMessage.error(res.message || "绑定失败");
- }
- } finally {
- bindLoading.value = false;
- }
- }
- async function handleUnbind(row: any) {
- if (!row._boundDevice) return;
- try {
- await ElMessageBox.confirm(
- `确认解除设备「${row._boundDevice.deviceId || row._boundDevice.name}」与 ERP 店铺「${row.shopName}」的绑定?`,
- "解除绑定",
- { type: "warning" }
- );
- const res = await unbindErpShop(row._boundDevice.id);
- if (res.code === 200) {
- ElMessage.success("解绑成功");
- await fetchList();
- } else {
- ElMessage.error(res.message || "解绑失败");
- }
- } catch {
- // 取消操作
- }
- }
- onMounted(() => {
- fetchList();
- });
- </script>
- <template>
- <div class="main">
- <!-- 搜索表单区域 -->
- <el-form
- :inline="true"
- :model="form"
- class="search-form bg-bg_color w-full pl-8 pt-[12px] overflow-auto"
- >
- <el-form-item label="绑定状态:">
- <el-select
- v-model="form.boundStatus"
- placeholder="全部"
- clearable
- class="w-[140px]!"
- @change="handleSearch"
- >
- <el-option label="已绑定" :value="1" />
- <el-option label="未绑定" :value="0" />
- </el-select>
- </el-form-item>
- <el-form-item label="店铺名称:">
- <el-input
- v-model="form.keyword"
- placeholder="搜索店铺名称或卖家账号"
- clearable
- class="w-[240px]!"
- @keyup.enter="handleSearch"
- />
- </el-form-item>
- <el-form-item>
- <el-button
- type="primary"
- :icon="useRenderIcon(Search)"
- :loading="loading"
- @click="handleSearch"
- >
- 搜索
- </el-button>
- <el-button :icon="useRenderIcon(Refresh)" @click="resetForm">
- 重置
- </el-button>
- </el-form-item>
- </el-form>
- <!-- 操作按钮 + 表格 -->
- <div class="px-6 pt-4">
- <div class="flex items-center justify-between mb-4">
- <h2 class="text-lg font-semibold">ERP 店铺管理</h2>
- <div class="flex items-center gap-2">
- <el-button
- type="primary"
- :icon="useRenderIcon(Refresh)"
- :loading="syncing"
- @click="handleSync"
- >
- 从 ERP 同步
- </el-button>
- <el-button
- type="success"
- :loading="autoBinding"
- @click="handleAutoBind"
- >
- 一键绑定
- </el-button>
- <el-button
- type="danger"
- :loading="unbindingAll"
- @click="handleUnbindAll"
- >
- 一键解绑
- </el-button>
- </div>
- </div>
- <el-table
- v-loading="loading"
- :data="dataList"
- border
- stripe
- style="width: 100%"
- empty-text="暂无数据,请先同步ERP店铺"
- :row-style="{ height: '64px' }"
- >
- <el-table-column prop="erpShopId" label="ERP店铺ID" width="160" align="center" />
- <el-table-column prop="shopName" label="店铺名称" min-width="180" show-overflow-tooltip />
- <el-table-column prop="nickName" label="卖家账号" min-width="160" show-overflow-tooltip />
- <el-table-column prop="shopCode" label="设备ID(shopCode)" width="180" align="center" show-overflow-tooltip />
- <el-table-column label="绑定设备" min-width="180">
- <template #default="{ row }">
- <el-popover
- v-if="row._boundDevice"
- trigger="hover"
- placement="right"
- :width="260"
- >
- <template #reference>
- <el-tag type="success" size="small" style="cursor:default">
- {{ row._boundDevice.deviceId }}-{{ row._boundDevice.name || '未命名' }}
- </el-tag>
- </template>
- <div class="text-sm">
- <div class="font-medium text-base mb-3">{{ row._boundDevice.name || row._boundDevice.deviceId }}</div>
- <div class="grid grid-cols-[60px_1fr] gap-y-2 gap-x-2 text-xs">
- <span class="text-gray-400">设备名称</span>
- <span>{{ row._boundDevice.name || '-' }}</span>
- <span class="text-gray-400">设备ID</span>
- <span>{{ row._boundDevice.deviceId || '-' }}</span>
- <span class="text-gray-400">所属门店</span>
- <span>{{ row._boundDevice.shopName || row._boundDevice.shopId || '-' }}</span>
- <span class="text-gray-400">地址</span>
- <span>{{ row._boundDevice.address || '-' }}</span>
- </div>
- </div>
- </el-popover>
- <span v-else class="text-gray-400">未绑定</span>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="160" fixed="right" align="center">
- <template #default="{ row }">
- <el-button
- v-if="!row._boundDevice"
- link
- type="primary"
- :icon="useRenderIcon(Link)"
- size="small"
- @click="openBindDialog(row)"
- >
- 绑定
- </el-button>
- <el-button
- v-else
- link
- type="danger"
- :icon="useRenderIcon(Remove)"
- size="small"
- @click="handleUnbind(row)"
- >
- 解绑
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- <div class="flex justify-end mt-4">
- <el-pagination
- v-model:current-page="pagination.currentPage"
- v-model:page-size="pagination.pageSize"
- :total="total"
- :page-sizes="[10, 20, 50, 100]"
- layout="total, sizes, prev, pager, next, jumper"
- background
- @current-change="handlePageChange"
- @size-change="handleSizeChange"
- />
- </div>
- </div>
- <!-- 绑定对话框 -->
- <el-dialog
- v-model="bindDialogVisible"
- title="绑定设备"
- width="500px"
- :close-on-click-modal="false"
- >
- <el-form label-width="100px">
- <el-form-item label="ERP 店铺">
- <el-input
- :model-value="currentErpShop?.shopName + ' (' + currentErpShop?.erpShopId + ')'"
- disabled
- />
- </el-form-item>
- <el-form-item label="选择设备">
- <el-select
- v-model="selectedDeviceId"
- placeholder="请选择要绑定的设备"
- clearable
- filterable
- class="w-full!"
- >
- <el-option
- v-for="d in deviceOptions"
- :key="d.id"
- :label="d.deviceId + ' - ' + (d.name || '未命名')"
- :value="d.id"
- />
- </el-select>
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="bindDialogVisible = false">取消</el-button>
- <el-button type="primary" :loading="bindLoading" @click="handleBind">确认绑定</el-button>
- </template>
- </el-dialog>
- </div>
- </template>
|