新后台
This commit is contained in:
parent
7eb2635fd0
commit
b61e5ebafe
@ -1,39 +1,89 @@
|
||||
//响应拦截器
|
||||
import axios from 'axios'; // 引入axios用于创建新请求
|
||||
|
||||
export function setupResponseInterceptors(instance) {
|
||||
instance.interceptors.response.use(
|
||||
response => {
|
||||
// 解析 response
|
||||
const { data, config } = response;
|
||||
// 用于存储正在刷新token的Promise
|
||||
let isRefreshing = false;
|
||||
// 存储等待token刷新的请求队列
|
||||
let requests = [];
|
||||
|
||||
if(response.data.code != 200){
|
||||
// 创建可追踪的业务逻辑错误
|
||||
const error = new Error(data.message || '业务逻辑错误');
|
||||
error.name = 'BusinessError'; // 自定义错误类型标识
|
||||
error.config = config; // 保留请求配置
|
||||
error.data = data; // 保留响应数据
|
||||
error.code = data.code; // 业务错误码
|
||||
instance.interceptors.response.use(
|
||||
response => {
|
||||
const { data } = response;
|
||||
if (data.code != 200) {
|
||||
const error = new Error(data.message || '业务逻辑错误');
|
||||
error.name = 'BusinessError';
|
||||
error.data = data;
|
||||
error.code = data.code;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return data;
|
||||
},
|
||||
async error => {
|
||||
// 获取原始请求配置
|
||||
const originalRequest = error.config;
|
||||
|
||||
//必须 return Promise.reject 才能触发 catch
|
||||
return Promise.reject(error);
|
||||
}
|
||||
// 处理响应数据格式
|
||||
return data
|
||||
},
|
||||
error => {
|
||||
// 统一错误处理
|
||||
if (error.response) {
|
||||
switch (error.response.status) {
|
||||
case 401:
|
||||
// 处理未授权
|
||||
break
|
||||
case 403:
|
||||
// 处理禁止访问
|
||||
break
|
||||
default:
|
||||
console.error('响应错误:', error)
|
||||
}
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
// 判断是否401错误(token过期)且没有在刷新中
|
||||
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||
// 标记该请求已尝试过重试
|
||||
originalRequest._retry = true;
|
||||
|
||||
// 如果当前没有在刷新token
|
||||
if (!isRefreshing) {
|
||||
isRefreshing = true;
|
||||
|
||||
try {
|
||||
// 从localStorage获取refreshToken
|
||||
const refreshToken = localStorage.getItem('refreshToken');
|
||||
|
||||
if (!refreshToken) {
|
||||
// 无refreshToken,跳转到登录页
|
||||
window.location.href = '/login';
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
// 调用刷新token接口
|
||||
const response = await axios.post('/admin/getAccessToken', { refreshToken });
|
||||
|
||||
// 获取新的token
|
||||
const { accessToken, refreshToken: newRefreshToken } = response.data.data;
|
||||
|
||||
// 更新本地存储
|
||||
localStorage.setItem('accessToken', accessToken);
|
||||
if (newRefreshToken) {
|
||||
localStorage.setItem('refreshToken', newRefreshToken);
|
||||
}
|
||||
|
||||
// 更新当前请求的Authorization头
|
||||
originalRequest.headers.Authorization = accessToken;
|
||||
|
||||
// 执行队列中的所有请求
|
||||
requests.forEach(cb => cb(accessToken));
|
||||
requests = [];
|
||||
|
||||
// 重新发送之前失败的请求
|
||||
return instance(originalRequest);
|
||||
} catch (refreshError) {
|
||||
// 刷新token失败,清除token并跳转到登录页
|
||||
localStorage.removeItem('accessToken');
|
||||
localStorage.removeItem('refreshToken');
|
||||
window.location.href = '/login';
|
||||
return Promise.reject(refreshError);
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
} else {
|
||||
// 如果已经在刷新中,将请求加入队列
|
||||
return new Promise(resolve => {
|
||||
requests.push(token => {
|
||||
originalRequest.headers.Authorization = token;
|
||||
resolve(instance(originalRequest));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
44
src/api/modules/order.js
Normal file
44
src/api/modules/order.js
Normal file
@ -0,0 +1,44 @@
|
||||
import request from '@/utils/axios'
|
||||
|
||||
// 获取订单列表
|
||||
export function getOrderList(params) {
|
||||
return request({
|
||||
url: '/wechatOrder/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取订单详情
|
||||
export function getOrderDetail(id) {
|
||||
return request({
|
||||
url: `/wechatOrder/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增订单
|
||||
export function addOrder(data) {
|
||||
return request({
|
||||
url: '/wechatOrder',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新订单
|
||||
export function updateOrder(data) {
|
||||
return request({
|
||||
url: '/wechatOrder',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除订单
|
||||
export function deleteOrder(id) {
|
||||
return request({
|
||||
url: `/wechatOrder/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
77
src/api/modules/shop.d.ts
vendored
Normal file
77
src/api/modules/shop.d.ts
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
// Shop API类型定义
|
||||
export interface ShopParams {
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
shopType?: string;
|
||||
shopGroup?: string;
|
||||
shopName?: string;
|
||||
shopAliasName?: string;
|
||||
shopAuthorize?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface ShopData {
|
||||
id: number;
|
||||
mallId?: string;
|
||||
shopNike?: string;
|
||||
shopType: string;
|
||||
shopGroup?: string;
|
||||
shopName: string;
|
||||
shopAliasName: string;
|
||||
shopAuthorize: string;
|
||||
status: string;
|
||||
account?: string;
|
||||
password?: string;
|
||||
isSynOrder: number;
|
||||
addTime?: string;
|
||||
expirationTime?: string;
|
||||
createdBy?: number;
|
||||
createdTime?: number;
|
||||
updatedBy?: number;
|
||||
updatedTime?: number;
|
||||
}
|
||||
|
||||
export interface ApiResponse<T> {
|
||||
code: number;
|
||||
message: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface PageResponse<T> {
|
||||
endRow: number;
|
||||
hasNextPage: boolean;
|
||||
hasPreviousPage: boolean;
|
||||
isFirstPage: boolean;
|
||||
isLastPage: boolean;
|
||||
list: T[];
|
||||
navigateFirstPage: number;
|
||||
navigateLastPage: number;
|
||||
navigatePages: number;
|
||||
navigatepageNums: number[];
|
||||
nextPage: number;
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
pages: number;
|
||||
prePage: number;
|
||||
size: number;
|
||||
startRow: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
declare function getShopList(params: ShopParams): Promise<{data: ApiResponse<PageResponse<ShopData>>}>;
|
||||
declare function getShopDetail(id: number): Promise<{data: ApiResponse<ShopData>}>;
|
||||
declare function addShop(data: ShopData): Promise<{data: ApiResponse<any>}>;
|
||||
declare function updateShop(data: ShopData): Promise<{data: ApiResponse<any>}>;
|
||||
declare function deleteShop(id: number): Promise<{data: ApiResponse<any>}>;
|
||||
declare function batchDeleteShop(ids: string): Promise<{data: ApiResponse<any>}>;
|
||||
declare function updateSyncOrderStatus(id: number, isSynOrder: number): Promise<{data: ApiResponse<any>}>;
|
||||
|
||||
export {
|
||||
getShopList,
|
||||
getShopDetail,
|
||||
addShop,
|
||||
updateShop,
|
||||
deleteShop,
|
||||
batchDeleteShop,
|
||||
updateSyncOrderStatus
|
||||
};
|
||||
66
src/api/modules/shop.js
Normal file
66
src/api/modules/shop.js
Normal file
@ -0,0 +1,66 @@
|
||||
import instance from '../../utils/axios.js'
|
||||
|
||||
// 获取店铺列表
|
||||
export const getShopList = async (params = {}) => {
|
||||
// 构造查询参数
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
// 添加分页参数
|
||||
if (params.pageNum) queryParams.append('pageNum', params.pageNum);
|
||||
if (params.pageSize) queryParams.append('pageSize', params.pageSize);
|
||||
|
||||
// 添加搜索参数
|
||||
if (params.shopType) queryParams.append('shopType', params.shopType);
|
||||
if (params.shopGroup) queryParams.append('shopGroup', params.shopGroup);
|
||||
if (params.shopName) queryParams.append('shopName', params.shopName);
|
||||
if (params.shopAliasName) queryParams.append('shopAliasName', params.shopAliasName);
|
||||
if (params.shopAuthorize) queryParams.append('shopAuthorize', params.shopAuthorize);
|
||||
if (params.status) queryParams.append('status', params.status);
|
||||
|
||||
const url = `/shop/list?${queryParams.toString()}`;
|
||||
return instance.get(url);
|
||||
};
|
||||
|
||||
// 获取店铺详情
|
||||
export const getShopDetail = (id) => {
|
||||
return instance.get(`/shop/${id}`);
|
||||
};
|
||||
|
||||
// 新增店铺
|
||||
export const addShop = (data) => {
|
||||
return instance.post('/shop', data);
|
||||
};
|
||||
|
||||
// 修改店铺
|
||||
export const updateShop = (data) => {
|
||||
return instance.put('/shop', data);
|
||||
};
|
||||
|
||||
// 删除店铺
|
||||
export const deleteShop = (id) => {
|
||||
return instance.delete(`/shop/${id}`);
|
||||
};
|
||||
|
||||
// 批量删除店铺
|
||||
export const batchDeleteShop = (ids) => {
|
||||
return instance.delete(`/shop/batch/${ids}`);
|
||||
};
|
||||
|
||||
// 更新同步订单状态
|
||||
export const updateSyncOrderStatus = (id, isSynOrder) => {
|
||||
return instance.put('/shop/syncOrder', {
|
||||
id,
|
||||
isSynOrder
|
||||
});
|
||||
};
|
||||
|
||||
// 为了向后兼容,也导出整个对象
|
||||
export const shopApi = {
|
||||
getShopList,
|
||||
getShopDetail,
|
||||
addShop,
|
||||
updateShop,
|
||||
deleteShop,
|
||||
batchDeleteShop,
|
||||
updateSyncOrderStatus
|
||||
};
|
||||
@ -24,7 +24,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { shallowRef } from 'vue'
|
||||
import {Document as DocIcon,Setting,User,Message,ShoppingCart } from '@element-plus/icons-vue'
|
||||
import {Document as DocIcon,Setting,User,Message,ShoppingCart,Shop,Connection } from '@element-plus/icons-vue'
|
||||
|
||||
const menuData = shallowRef([{
|
||||
title: '系统管理',
|
||||
@ -80,6 +80,19 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '店铺管理',
|
||||
path: '/shop',
|
||||
icon: Shop,
|
||||
children: [{
|
||||
title: '店铺列表',
|
||||
path: '/shop/list',
|
||||
children: [{
|
||||
title: '店铺列表',
|
||||
path: '/shop/list'
|
||||
}]
|
||||
}]
|
||||
},
|
||||
{
|
||||
title: '工具管理',
|
||||
path: '/tools',
|
||||
@ -128,20 +141,7 @@
|
||||
path: '/useModule/vas/list'
|
||||
}]
|
||||
}]
|
||||
},
|
||||
{
|
||||
title: '订单管理',
|
||||
path: '/order',
|
||||
icon: ShoppingCart,
|
||||
children: [{
|
||||
title: '微信订单',
|
||||
path: '/order/wechat',
|
||||
children: [{
|
||||
title: '订单列表',
|
||||
path: '/order/wechat/list'
|
||||
}]
|
||||
}]
|
||||
},
|
||||
}
|
||||
// 更多菜单...
|
||||
])
|
||||
</script>
|
||||
|
||||
@ -33,6 +33,11 @@ const routes = [{
|
||||
component: () => import('@/views/User/Edit.vue'),
|
||||
meta: { title: '新增用户' }
|
||||
},
|
||||
{
|
||||
path: '/shop/list',
|
||||
component: () => import('@/views/Shop/index.vue'),
|
||||
meta: { title: '店铺列表' }
|
||||
},
|
||||
{
|
||||
path: '/tools/cards/list',
|
||||
component: () => import('@/views/Tools/Cards/List.vue'),
|
||||
@ -62,6 +67,11 @@ const routes = [{
|
||||
path: '/order/wechat/list',
|
||||
component: () => import('@/views/order/wechat/list.vue'),
|
||||
meta: { title: '订单列表' }
|
||||
},
|
||||
{
|
||||
path: '/websocket/demo',
|
||||
component: () => import('@/views/websocket/index.vue'),
|
||||
meta: { title: 'WebSocket演示' }
|
||||
}
|
||||
]
|
||||
}]
|
||||
|
||||
57
src/utils/request.js
Normal file
57
src/utils/request.js
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 外部API请求工具
|
||||
*/
|
||||
|
||||
// 检查字符串是否为URL
|
||||
const isValidUrl = (string) => {
|
||||
try {
|
||||
new URL(string);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 发送GET请求到外部API
|
||||
export const fetchExternalApi = async (url) => {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': '*/*',
|
||||
},
|
||||
// 禁用缓存
|
||||
cache: 'no-cache',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
|
||||
// 先尝试获取文本响应
|
||||
const text = await response.text();
|
||||
|
||||
// 如果文本是URL,直接返回
|
||||
if (isValidUrl(text)) {
|
||||
console.log('直接返回URL:', text);
|
||||
return { url: text.trim() };
|
||||
}
|
||||
|
||||
// 尝试解析为JSON
|
||||
try {
|
||||
const jsonData = JSON.parse(text);
|
||||
return jsonData;
|
||||
} catch (jsonError) {
|
||||
// 如果不是JSON,则作为文本返回
|
||||
return { text };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('外部API请求失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
fetchExternalApi,
|
||||
isValidUrl
|
||||
};
|
||||
@ -592,39 +592,66 @@
|
||||
<el-table-column type="selection" align="center" width="55" />
|
||||
<el-table-column prop="title" align="center" label="标题" width="120" />
|
||||
<el-table-column prop="settled_cost_key" align="center" label="入驻标识" width="180" />
|
||||
<el-table-column prop="kickback_type" align="center" label="佣金类型" width="100">
|
||||
<template #default="{ row }">{{ formatKickbackType(row.kickback_type) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="佣金值" width="180">
|
||||
<!-- 小程序收费佣金 -->
|
||||
<el-table-column align="center" label="小程序佣金" width="180">
|
||||
<template #default="{ row }">
|
||||
<div>
|
||||
<div v-if="[1, 3].includes(row.kickback_type)">
|
||||
提点: {{ parseKickbackValue(row.kickback_value, 'point') }}%
|
||||
<div v-if="parseConstraint(row.constraint_json) && parseConstraint(row.constraint_json).miniapp">
|
||||
<div>
|
||||
类型: {{ formatKickbackType(parseConstraint(row.constraint_json).miniapp.kickback_type || 0) }}
|
||||
</div>
|
||||
<div v-if="[2, 3].includes(row.kickback_type)">
|
||||
固定费用: {{ parseKickbackValue(row.kickback_value, 'fixed') }}元
|
||||
<div v-if="[1, 3].includes(parseConstraint(row.constraint_json).miniapp.kickback_type || 0)">
|
||||
提点: {{ parseKickbackValueJson(parseConstraint(row.constraint_json).miniapp.kickback_value || '{}', 'point') }}%
|
||||
</div>
|
||||
<div v-if="![1, 2, 3].includes(row.kickback_type)">
|
||||
{{ row.kickback_value }}
|
||||
<div v-if="[2, 3].includes(parseConstraint(row.constraint_json).miniapp.kickback_type || 0)">
|
||||
固费: {{ parseKickbackValueJson(parseConstraint(row.constraint_json).miniapp.kickback_value || '{}', 'fixed') }}元
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="resource_cost_type" align="center" label="资源费类型" width="120">
|
||||
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
{{ formatResourceCostType(row.resource_cost_type) }}
|
||||
<el-button-group>
|
||||
<el-button type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button type="danger" size="small" @click="handleDelete(row)" :disabled="row.is_del === 1">
|
||||
删除
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="资源费值/服务费率" width="180">
|
||||
<!-- 孔夫子店铺佣金 -->
|
||||
<el-table-column align="center" label="孔夫子佣金" width="180">
|
||||
<template #default="{ row }">
|
||||
<div>
|
||||
<div v-if="[1, 3].includes(row.resource_cost_type)">
|
||||
服务费率: {{ row.service_rate }}%
|
||||
<div v-if="parseConstraint(row.constraint_json) && parseConstraint(row.constraint_json).kongfz">
|
||||
<div>
|
||||
类型: {{ formatKickbackType(parseConstraint(row.constraint_json).kongfz.kickback_type || 0) }}
|
||||
</div>
|
||||
<div v-if="[2, 3].includes(row.resource_cost_type)">
|
||||
资源费值: {{ row.resource_cost_value }}元
|
||||
<div v-if="[1, 3].includes(parseConstraint(row.constraint_json).kongfz.kickback_type || 0)">
|
||||
提点: {{ parseKickbackValueJson(parseConstraint(row.constraint_json).kongfz.kickback_value || '{}', 'point') }}%
|
||||
</div>
|
||||
<div v-if="[2, 3].includes(parseConstraint(row.constraint_json).kongfz.kickback_type || 0)">
|
||||
固费: {{ parseKickbackValueJson(parseConstraint(row.constraint_json).kongfz.kickback_value || '{}', 'fixed') }}元
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 资源费分级佣金 -->
|
||||
<el-table-column align="center" label="资源费佣金" width="180">
|
||||
<template #default="{ row }">
|
||||
<div v-if="parseConstraint(row.constraint_json) && parseConstraint(row.constraint_json).resource_tier">
|
||||
<div>
|
||||
类型: {{ formatKickbackType(parseConstraint(row.constraint_json).resource_tier.kickback_type || 0) }}
|
||||
</div>
|
||||
<div v-if="[1, 3].includes(parseConstraint(row.constraint_json).resource_tier.kickback_type || 0)">
|
||||
提点: {{ parseKickbackValueJson(parseConstraint(row.constraint_json).resource_tier.kickback_value || '{}', 'point') }}%
|
||||
</div>
|
||||
<div v-if="[2, 3].includes(parseConstraint(row.constraint_json).resource_tier.kickback_type || 0)">
|
||||
固费: {{ parseKickbackValueJson(parseConstraint(row.constraint_json).resource_tier.kickback_value || '{}', 'fixed') }}元
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="price" align="center" label="价格(元)" width="120" />
|
||||
@ -639,16 +666,6 @@
|
||||
{{ row.is_del === 0 ? '正常' : '已删除' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button-group>
|
||||
<el-button type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button type="danger" size="small" @click="handleDelete(row)" :disabled="row.is_del === 1">
|
||||
删除
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
@ -679,6 +696,11 @@
|
||||
<el-form-item label="入驻标识" prop="settled_cost_key">
|
||||
<el-input v-model="formData.settled_cost_key" placeholder="请输入入驻标识" :disabled="dialogType === 'edit'" />
|
||||
</el-form-item>
|
||||
<el-form-item label="会员类型" prop="member_type">
|
||||
<el-select v-model="formData.member_type" placeholder="请选择会员类型" clearable>
|
||||
<el-option label="小程序" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="最小本数" prop="books_count_min">
|
||||
<el-input-number v-model="formData.books_count_min" :min="0" :precision="0" placeholder="请输入最小本数" />
|
||||
</el-form-item>
|
||||
@ -1660,6 +1682,7 @@ const formData = reactive({
|
||||
constraint_json: '',
|
||||
books_count_min: 0,
|
||||
books_count_max: 0,
|
||||
member_type: 1, // 添加会员类型,默认为1(小程序)
|
||||
kickback_type: 0,
|
||||
kickback_value: '',
|
||||
kickback_point: 0,
|
||||
@ -1887,6 +1910,7 @@ const resetFormData = () => {
|
||||
constraint_json: '',
|
||||
books_count_min: 0,
|
||||
books_count_max: 0,
|
||||
member_type: 1, // 添加会员类型,默认为1(小程序)
|
||||
kickback_type: 0,
|
||||
kickback_value: '',
|
||||
kickback_point: 0,
|
||||
@ -2202,6 +2226,7 @@ const handleEdit = (row: TableItem) => {
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
settled_cost_key: row.settled_cost_key,
|
||||
member_type: constraintData.member_type || 1, // 设置会员类型,默认为1(小程序)
|
||||
constraint_json: constraintData,
|
||||
books_count_min: min,
|
||||
books_count_max: max,
|
||||
@ -2235,6 +2260,7 @@ const handleSubmit = async () => {
|
||||
const constraintObj = {
|
||||
books_count_min: formData.books_count_min,
|
||||
books_count_max: formData.books_count_max,
|
||||
member_type: formData.member_type, // 添加会员类型
|
||||
|
||||
// 小程序收费
|
||||
miniapp: {
|
||||
|
||||
608
src/views/Shop/index.vue
Normal file
608
src/views/Shop/index.vue
Normal file
@ -0,0 +1,608 @@
|
||||
<template>
|
||||
<div class="list-container">
|
||||
<div class="search-area">
|
||||
<el-form :model="queryParams" inline>
|
||||
<el-form-item label="店铺类型">
|
||||
<el-select v-model="queryParams.shopType" placeholder="请选择店铺类型" clearable>
|
||||
<el-option label="拼多多" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组">
|
||||
<el-input v-model="queryParams.shopGroup" placeholder="请输入分组" />
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺名称">
|
||||
<el-input v-model="queryParams.shopName" placeholder="请输入店铺名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="三方名称">
|
||||
<el-input v-model="queryParams.shopAliasName" placeholder="请输入三方名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否授权">
|
||||
<el-select v-model="queryParams.shopAuthorize" placeholder="请选择是否授权" clearable>
|
||||
<el-option label="已授权" value="1" />
|
||||
<el-option label="未授权" value="0" />
|
||||
<el-option label="已过期" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择店铺状态" clearable>
|
||||
<el-option label="正常" value="0" />
|
||||
<el-option label="停用" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleSearch">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="action-bar">
|
||||
<div class="action-left">
|
||||
<el-button type="primary" @click="handleAdd">新增</el-button>
|
||||
<el-button type="primary" @click="handleEdit">编辑</el-button>
|
||||
<el-button type="danger" @click="handleDelete">删除</el-button>
|
||||
</div>
|
||||
<div class="action-right">
|
||||
<RefreshButton @refresh="getShopListData" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="shopList"
|
||||
@selection-change="handleSelectionChange"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
row-key="id"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="店铺编码" prop="id" align="center" />
|
||||
<el-table-column label="店铺类型" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.shopType === '1' ? '拼多多' : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分组" prop="shopGroup" align="center" />
|
||||
<el-table-column label="店铺名称" prop="shopName" align="center" />
|
||||
<el-table-column label="三方名称" prop="shopAliasName" align="center" />
|
||||
<el-table-column label="是否授权" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getAuthorizeTagType(scope.row.shopAuthorize)" v-if="scope.row.shopAuthorize === '1'">
|
||||
{{ getAuthorizeText(scope.row.shopAuthorize) }}
|
||||
</el-tag>
|
||||
<el-button
|
||||
v-else
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="handleAuthorize(scope.row)"
|
||||
>
|
||||
授权
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="到期时间" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.expirationTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="添加时间" prop="addTime" align="center" />
|
||||
<el-table-column label="店铺状态" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">
|
||||
{{ scope.row.status === '0' ? '正常' : '停用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="同步订单" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.isSynOrder"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
class="ml-2"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@change="handleSyncOrderChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设置" align="center" width="60">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" icon="Setting" circle plain size="small" @click="handleSetting(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="220" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button size="small" type="primary" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === '0'"
|
||||
size="small"
|
||||
type="warning"
|
||||
@click="handleChangeStatus(scope.row, '1')"
|
||||
>停用</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === '1'"
|
||||
size="small"
|
||||
type="success"
|
||||
@click="handleChangeStatus(scope.row, '0')"
|
||||
>启用</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 添加/编辑对话框 -->
|
||||
<el-dialog
|
||||
:title="dialog.title"
|
||||
v-model="dialog.visible"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
append-to-body
|
||||
>
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
|
||||
<el-form-item label="店铺类型" prop="shopType">
|
||||
<el-select v-model="form.shopType" placeholder="请选择店铺类型" style="width: 100%">
|
||||
<el-option label="拼多多" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组" prop="shopGroup">
|
||||
<el-input v-model="form.shopGroup" placeholder="请输入分组" />
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺名称" prop="shopName">
|
||||
<el-input v-model="form.shopName" placeholder="请输入店铺名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="三方名称" prop="shopAliasName">
|
||||
<el-input v-model="form.shopAliasName" placeholder="请输入三方名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺ID" prop="mallId">
|
||||
<el-input v-model="form.mallId" placeholder="请输入三方店铺ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="万里牛ID" prop="shopNike">
|
||||
<el-input v-model="form.shopNike" placeholder="请输入万里牛系统ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="第三方账号" prop="account">
|
||||
<el-input v-model="form.account" placeholder="请输入第三方平台账号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="第三方密码" prop="password">
|
||||
<el-input v-model="form.password" type="password" placeholder="请输入第三方平台密码" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="同步订单" prop="isSynOrder">
|
||||
<el-switch
|
||||
v-model="form.isSynOrder"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="店铺状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio label="0">正常</el-radio>
|
||||
<el-radio label="1">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
// import { shopApi } from '@/api'
|
||||
import {
|
||||
getShopList,
|
||||
getShopDetail,
|
||||
addShop,
|
||||
updateShop,
|
||||
deleteShop,
|
||||
batchDeleteShop,
|
||||
updateSyncOrderStatus
|
||||
} from '@/api/modules/shop'
|
||||
import { fetchExternalApi } from '@/utils/request'
|
||||
import RefreshButton from '@/components/RefreshButton.vue'
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
shopType: '',
|
||||
shopGroup: '',
|
||||
shopName: '',
|
||||
shopAliasName: '',
|
||||
shopAuthorize: '',
|
||||
status: '',
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
})
|
||||
|
||||
// 表格数据
|
||||
const loading = ref(false)
|
||||
const shopList = ref([])
|
||||
const total = ref(0)
|
||||
const selectedRows = ref<any[]>([])
|
||||
|
||||
// 对话框相关
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: ''
|
||||
})
|
||||
|
||||
// 表单对象
|
||||
const formRef = ref()
|
||||
const form = reactive({
|
||||
id: undefined,
|
||||
mallId: '',
|
||||
shopNike: '',
|
||||
shopType: '1',
|
||||
shopGroup: '',
|
||||
shopName: '',
|
||||
shopAliasName: '',
|
||||
shopAuthorize: '0',
|
||||
status: '0',
|
||||
account: '',
|
||||
password: '',
|
||||
isSynOrder: 0
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
shopType: [{ required: true, message: '请选择店铺类型', trigger: 'change' }],
|
||||
shopName: [{ required: true, message: '请输入店铺名称', trigger: 'blur' }],
|
||||
shopAliasName: [{ required: true, message: '请输入三方名称', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
// 初始化方法
|
||||
onMounted(() => {
|
||||
getShopListData()
|
||||
})
|
||||
|
||||
// 获取授权状态标签类型
|
||||
const getAuthorizeTagType = (authorize: string) => {
|
||||
switch (authorize) {
|
||||
case '1': return 'success'
|
||||
case '0': return 'info'
|
||||
case '2': return 'warning'
|
||||
default: return 'info'
|
||||
}
|
||||
}
|
||||
|
||||
// 获取授权状态文本
|
||||
const getAuthorizeText = (authorize: string) => {
|
||||
switch (authorize) {
|
||||
case '1': return '已授权'
|
||||
case '0': return '未授权'
|
||||
case '2': return '已过期'
|
||||
default: return '未知'
|
||||
}
|
||||
}
|
||||
|
||||
// 获取店铺列表
|
||||
const getShopListData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getShopList(queryParams)
|
||||
console.log("res", res)
|
||||
if (res.code === 200) {
|
||||
shopList.value = res.data.list
|
||||
total.value = res.data.total
|
||||
} else {
|
||||
ElMessage.error(res.message || '获取数据失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('获取店铺列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索按钮操作
|
||||
const handleSearch = () => {
|
||||
queryParams.pageNum = 1
|
||||
getShopListData()
|
||||
}
|
||||
|
||||
// 重置按钮操作
|
||||
const handleReset = () => {
|
||||
queryParams.shopType = ''
|
||||
queryParams.shopGroup = ''
|
||||
queryParams.shopName = ''
|
||||
queryParams.shopAliasName = ''
|
||||
queryParams.shopAuthorize = ''
|
||||
queryParams.status = ''
|
||||
queryParams.pageNum = 1
|
||||
getShopListData()
|
||||
}
|
||||
|
||||
// 分页相关操作
|
||||
const handleSizeChange = (val: number) => {
|
||||
queryParams.pageSize = val
|
||||
getShopListData()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val: number) => {
|
||||
queryParams.pageNum = val
|
||||
getShopListData()
|
||||
}
|
||||
|
||||
// 选择行事件
|
||||
const handleSelectionChange = (selection) => {
|
||||
selectedRows.value = selection
|
||||
}
|
||||
|
||||
// 打开添加对话框
|
||||
const handleAdd = () => {
|
||||
dialog.title = '添加店铺'
|
||||
dialog.visible = true
|
||||
form.id = undefined
|
||||
form.mallId = ''
|
||||
form.shopNike = ''
|
||||
form.shopType = '1'
|
||||
form.shopGroup = ''
|
||||
form.shopName = ''
|
||||
form.shopAliasName = ''
|
||||
form.shopAuthorize = '0'
|
||||
form.status = '0'
|
||||
form.account = ''
|
||||
form.password = ''
|
||||
form.isSynOrder = 0
|
||||
}
|
||||
|
||||
// 修改按钮操作
|
||||
const handleEdit = (row) => {
|
||||
dialog.title = '编辑店铺'
|
||||
dialog.visible = true
|
||||
|
||||
if (row) {
|
||||
// 如果传递了行数据,则编辑该行
|
||||
const shopId = row.id
|
||||
if (shopId) {
|
||||
getShopDetail(shopId).then(res => {
|
||||
if (res.data.code === 200) {
|
||||
Object.assign(form, res.data.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// 否则检查是否有选中行
|
||||
if (selectedRows.value.length !== 1) {
|
||||
ElMessage.warning('请选择一条记录进行修改')
|
||||
dialog.visible = false
|
||||
return
|
||||
}
|
||||
|
||||
const shopId = selectedRows.value[0].id
|
||||
if (shopId) {
|
||||
getShopDetail(shopId).then(res => {
|
||||
if (res.data.code === 200) {
|
||||
Object.assign(form, res.data.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除按钮操作
|
||||
const handleDelete = (row) => {
|
||||
if (row) {
|
||||
// 删除单条记录
|
||||
ElMessageBox.confirm('确认删除该店铺记录吗?', '警告', {
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
if (row.id) {
|
||||
deleteShop(row.id).then(res => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage.success('删除成功')
|
||||
getShopListData()
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(() => {})
|
||||
} else {
|
||||
// 批量删除
|
||||
if (selectedRows.value.length === 0) {
|
||||
ElMessage.warning('请至少选择一条记录')
|
||||
return
|
||||
}
|
||||
|
||||
ElMessageBox.confirm(`确认删除选中的${selectedRows.value.length}条店铺记录吗?`, '警告', {
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
const ids = selectedRows.value.map(item => item.id).filter(id => id !== undefined)
|
||||
if (ids.length > 0) {
|
||||
batchDeleteShop(ids.join(',')).then(res => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage.success('批量删除成功')
|
||||
getShopListData()
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
// 设置按钮操作
|
||||
const handleSetting = (row) => {
|
||||
ElMessage.info(`设置店铺: ${row.shopName}`)
|
||||
// 实现店铺设置功能
|
||||
}
|
||||
|
||||
// 同步订单状态变更
|
||||
const handleSyncOrderChange = (row) => {
|
||||
const status = row.isSynOrder === 1 ? '开启' : '关闭'
|
||||
if (row.id) {
|
||||
updateSyncOrderStatus(row.id, row.isSynOrder).then(res => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage.success(`${status}同步订单成功`)
|
||||
} else {
|
||||
ElMessage.error(`${status}同步订单失败`)
|
||||
// 恢复原状态
|
||||
row.isSynOrder = row.isSynOrder === 1 ? 0 : 1
|
||||
}
|
||||
}).catch(() => {
|
||||
// 恢复原状态
|
||||
row.isSynOrder = row.isSynOrder === 1 ? 0 : 1
|
||||
ElMessage.error(`${status}同步订单失败`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 处理授权操作
|
||||
const handleAuthorize = async (row) => {
|
||||
try {
|
||||
// 显示加载提示
|
||||
ElMessage.info('正在获取授权链接,请稍候...');
|
||||
|
||||
// 调用API获取授权链接
|
||||
const apiUrl = `https://api.buzhiyushu.cn/huidiao/pdd/toPddGetCode?id=${row.id}&type=4`;
|
||||
const result = await fetchExternalApi(apiUrl);
|
||||
|
||||
// 处理不同类型的响应
|
||||
let authUrl = null;
|
||||
|
||||
if (result) {
|
||||
if (result.url) {
|
||||
// 如果响应中直接包含url字段
|
||||
authUrl = result.url;
|
||||
} else if (typeof result === 'string' && result.includes('http')) {
|
||||
// 如果响应直接是URL字符串
|
||||
authUrl = result;
|
||||
}
|
||||
}
|
||||
|
||||
if (authUrl) {
|
||||
console.log('授权URL:', authUrl);
|
||||
|
||||
// 打开新窗口进行授权
|
||||
const newTab = window.open(authUrl, '_blank');
|
||||
|
||||
// 如果无法打开新窗口,则尝试直接跳转
|
||||
if (!newTab || newTab.closed || typeof newTab.closed === 'undefined') {
|
||||
window.location.href = authUrl;
|
||||
}
|
||||
} else {
|
||||
console.error('无法解析授权链接:', result);
|
||||
ElMessage.error('获取授权链接失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('授权请求失败:', error);
|
||||
ElMessage.error('授权请求失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 处理状态变更
|
||||
const handleChangeStatus = async (row, newStatus) => {
|
||||
const statusText = newStatus === '0' ? '启用' : '停用';
|
||||
|
||||
ElMessageBox.confirm(`确认要${statusText}该店铺吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
// 更新状态的API调用
|
||||
const updatedData = { ...row, status: newStatus };
|
||||
await updateShop(updatedData);
|
||||
ElMessage.success(`${statusText}成功`);
|
||||
getShopListData(); // 刷新数据
|
||||
} catch (error) {
|
||||
console.error(`${statusText}失败:`, error);
|
||||
ElMessage.error(`${statusText}失败`);
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const submitForm = () => {
|
||||
formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
if (form.id) {
|
||||
// 编辑
|
||||
updateShop(form).then(res => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage.success('修改成功')
|
||||
dialog.visible = false
|
||||
getShopListData()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 新增
|
||||
addShop(form).then(res => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage.success('新增成功')
|
||||
dialog.visible = false
|
||||
getShopListData()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.list-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.search-area {
|
||||
margin-bottom: 20px;
|
||||
padding: 18px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.search-area :deep(.el-form-item) {
|
||||
margin-right: 18px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.action-left {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.action-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
@ -46,7 +46,7 @@ export default defineConfig({
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://127.0.0.1:8089',
|
||||
// target: 'http://146.56.227.42',
|
||||
// target: 'http://146.56.227.42:8089',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/,''),
|
||||
// 如需处理WebSocket
|
||||
|
||||
Loading…
Reference in New Issue
Block a user