newadmin
This commit is contained in:
parent
fe89d930f1
commit
cec79556cc
73
src/api/modules/district.js
Normal file
73
src/api/modules/district.js
Normal file
@ -0,0 +1,73 @@
|
||||
import request from '@/utils/axios'
|
||||
|
||||
/**
|
||||
* 获取所有省级数据
|
||||
*/
|
||||
export function getProvinces() {
|
||||
return request({
|
||||
url: '/district/provinces',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据省份ID获取城市列表
|
||||
*/
|
||||
export function getCitiesByProvinceId(provinceId) {
|
||||
return request({
|
||||
url: `/district/cities/${provinceId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据城市ID获取区县列表
|
||||
*/
|
||||
export function getDistrictsByCityId(cityId) {
|
||||
return request({
|
||||
url: `/district/districts/${cityId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取省级市级树形结构
|
||||
*/
|
||||
export function getDistrictTree() {
|
||||
return request({
|
||||
url: '/district/getDistrictTree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增物流模板
|
||||
*/
|
||||
export function createTemplate(data) {
|
||||
return request({
|
||||
url: '/logistics/logistics',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取运费信息
|
||||
*/
|
||||
export function getFreInfo(id) {
|
||||
return request({
|
||||
url: '/logistics/logistics/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新物流模板
|
||||
*/
|
||||
export function UpdateTemplate(data) {
|
||||
return request({
|
||||
url: '/logistics/logistics',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
65
src/api/modules/logistics.js
Normal file
65
src/api/modules/logistics.js
Normal file
@ -0,0 +1,65 @@
|
||||
import request from '@/utils/axios'
|
||||
|
||||
/**
|
||||
* 查询物流管理列表
|
||||
*/
|
||||
export function listLogistics(query) {
|
||||
return request({
|
||||
url: '/logistics/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询物流管理详细
|
||||
*/
|
||||
export function getLogistics(id) {
|
||||
return request({
|
||||
url: '/logistics/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增物流管理
|
||||
*/
|
||||
export function addLogistics(data) {
|
||||
return request({
|
||||
url: '/logistics',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改物流管理
|
||||
*/
|
||||
export function updateLogistics(data) {
|
||||
return request({
|
||||
url: '/logistics',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除物流管理
|
||||
*/
|
||||
export function delLogistics(id) {
|
||||
return request({
|
||||
url: '/logistics/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出物流管理
|
||||
*/
|
||||
export function exportLogistics(query) {
|
||||
return request({
|
||||
url: '/logistics/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
64
src/api/modules/shelves.js
Normal file
64
src/api/modules/shelves.js
Normal file
@ -0,0 +1,64 @@
|
||||
import request from '@/utils/axios'
|
||||
|
||||
/**
|
||||
* 获取货区名称列表
|
||||
*/
|
||||
export function depotNameList() {
|
||||
return request({
|
||||
url: '/depot/nameList',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询货架列表
|
||||
*/
|
||||
export function listShelves(query) {
|
||||
return request({
|
||||
url: '/shelves/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询货架详细
|
||||
*/
|
||||
export function getShelves(id) {
|
||||
return request({
|
||||
url: '/shelves/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增货架
|
||||
*/
|
||||
export function addShelves(data) {
|
||||
return request({
|
||||
url: '/shelves',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改货架
|
||||
*/
|
||||
export function updateShelves(data) {
|
||||
return request({
|
||||
url: '/shelves',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除货架
|
||||
*/
|
||||
export function delShelves(id) {
|
||||
return request({
|
||||
url: '/shelves/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
16
src/api/modules/userLogin.js
Normal file
16
src/api/modules/userLogin.js
Normal file
@ -0,0 +1,16 @@
|
||||
import instance from '../../utils/axios.js'
|
||||
|
||||
// 用户登录相关API
|
||||
const userLoginApi = {
|
||||
// 用户登录 - 直接发送FormData
|
||||
userLogin: (data) => {
|
||||
return instance.post('/userLogin/login', data);
|
||||
},
|
||||
// 获取用户信息
|
||||
getUserInfo: (accessToken) => instance.get('/userLogin/getUserInfo', {
|
||||
params: { accessToken }
|
||||
})
|
||||
};
|
||||
|
||||
// 导出模块
|
||||
export { userLoginApi };
|
||||
19
src/config/api.js
Normal file
19
src/config/api.js
Normal file
@ -0,0 +1,19 @@
|
||||
// API配置
|
||||
const config = {
|
||||
// 从环境变量获取API基础URL
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL || '',
|
||||
timeout: 10000
|
||||
}
|
||||
|
||||
// 获取当前环境配置
|
||||
export const getApiConfig = () => {
|
||||
return config
|
||||
}
|
||||
|
||||
// 获取完整的API URL
|
||||
export const getApiUrl = (path) => {
|
||||
const { baseURL } = getApiConfig()
|
||||
return `${baseURL}${path}`
|
||||
}
|
||||
|
||||
export default config
|
||||
44
src/directives/permission.js
Normal file
44
src/directives/permission.js
Normal file
@ -0,0 +1,44 @@
|
||||
import { hasPermission, hasAnyPermission } from '@/utils/permission'
|
||||
|
||||
/**
|
||||
* 权限指令
|
||||
* v-permission="'system:user:add'" 单个权限
|
||||
* v-permission="['system:user:add', 'system:user:update']" 多个权限(任意一个)
|
||||
*/
|
||||
export const permission = {
|
||||
mounted(el, binding) {
|
||||
const { value } = binding
|
||||
|
||||
if (value) {
|
||||
let hasAuth = false
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
hasAuth = hasAnyPermission(value)
|
||||
} else {
|
||||
hasAuth = hasPermission(value)
|
||||
}
|
||||
|
||||
if (!hasAuth) {
|
||||
el.parentNode && el.parentNode.removeChild(el)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限指令(所有权限都需要)
|
||||
* v-permission-all="['system:user:add', 'system:user:update']"
|
||||
*/
|
||||
export const permissionAll = {
|
||||
mounted(el, binding) {
|
||||
const { value } = binding
|
||||
|
||||
if (value && Array.isArray(value)) {
|
||||
const hasAuth = value.every(code => hasPermission(code))
|
||||
|
||||
if (!hasAuth) {
|
||||
el.parentNode && el.parentNode.removeChild(el)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
198
src/layout/DynamicSidebar.vue
Normal file
198
src/layout/DynamicSidebar.vue
Normal file
@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<el-menu router :default-active="$route.path" :collapse="false" unique-opened background-color="#304156"
|
||||
text-color="#bfcbd9" active-text-color="#409EFF">
|
||||
<template v-for="item in filteredMenuTree" :key="item.id">
|
||||
<!-- 有子菜单的情况 -->
|
||||
<el-sub-menu v-if="item.children && item.children.length > 0" :index="item.path || item.id.toString()">
|
||||
<template #title>
|
||||
<el-icon v-if="item.icon">
|
||||
<component :is="getIcon(item.icon)" />
|
||||
</el-icon>
|
||||
<span>{{ item.name }}</span>
|
||||
</template>
|
||||
<el-menu-item v-for="child in item.children" :key="child.id" :index="child.path">
|
||||
{{ child.name }}
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
|
||||
<!-- 单个菜单项 -->
|
||||
<el-menu-item v-else-if="item.path" :index="item.path">
|
||||
<el-icon v-if="item.icon">
|
||||
<component :is="getIcon(item.icon)" />
|
||||
</el-icon>
|
||||
<span>{{ item.name }}</span>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import {
|
||||
Setting, Shop, Notebook, Monitor, Document as DocIcon,
|
||||
User, Message, ShoppingCart, Connection, Box,
|
||||
TrendCharts, HomeFilled
|
||||
} from '@element-plus/icons-vue'
|
||||
import { getUserMenuTree } from '@/api/permission'
|
||||
import { hasPermission, getUserPermissions } from '@/utils/permission'
|
||||
|
||||
const userMenuTree = ref([])
|
||||
|
||||
// 扩展图标映射
|
||||
const iconMap = {
|
||||
'Setting': Setting,
|
||||
'Shop': Shop,
|
||||
'Notebook': Notebook,
|
||||
'Monitor': Monitor,
|
||||
'Document': DocIcon,
|
||||
'User': User,
|
||||
'Message': Message,
|
||||
'ShoppingCart': ShoppingCart,
|
||||
'Connection': Connection,
|
||||
'Box': Box,
|
||||
'TrendCharts': TrendCharts,
|
||||
'HomeFilled': HomeFilled
|
||||
}
|
||||
|
||||
const getIcon = (iconName: string) => {
|
||||
return iconMap[iconName] || Setting
|
||||
}
|
||||
|
||||
// 过滤菜单树,只显示有权限的菜单项
|
||||
const filteredMenuTree = computed(() => {
|
||||
return filterMenuByPermission(userMenuTree.value)
|
||||
})
|
||||
|
||||
const filterMenuByPermission = (menuItems: any[]): any[] => {
|
||||
if (!menuItems || menuItems.length === 0) return []
|
||||
|
||||
return menuItems.filter(item => {
|
||||
// 如果有子菜单,先递归过滤子菜单
|
||||
if (item.children && item.children.length > 0) {
|
||||
const filteredChildren = filterMenuByPermission(item.children)
|
||||
|
||||
// 如果是目录类型(type=1),只有当有可显示的子菜单时才显示
|
||||
if (item.type === 1) {
|
||||
if (filteredChildren.length > 0) {
|
||||
item.children = filteredChildren
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 如果是菜单类型(type=2),检查自身权限
|
||||
if (item.type === 2) {
|
||||
if (item.code && !hasPermission(item.code)) {
|
||||
return false
|
||||
}
|
||||
item.children = filteredChildren
|
||||
return true
|
||||
}
|
||||
|
||||
item.children = filteredChildren
|
||||
return filteredChildren.length > 0
|
||||
}
|
||||
|
||||
// 没有子菜单的情况
|
||||
// 如果是目录类型但没有子菜单,不显示
|
||||
if (item.type === 1) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 如果是菜单类型(type=2),检查权限
|
||||
if (item.type === 2) {
|
||||
// 如果有权限编码,检查权限
|
||||
if (item.code) {
|
||||
return hasPermission(item.code)
|
||||
}
|
||||
// 如果没有权限编码,默认显示
|
||||
return true
|
||||
}
|
||||
|
||||
// 其他类型默认不显示
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
const loadUserMenu = async () => {
|
||||
try {
|
||||
// 确保权限数据已经加载
|
||||
const permissions = getUserPermissions()
|
||||
if (permissions.length === 0) {
|
||||
console.log('权限数据未加载,等待权限初始化...')
|
||||
// 如果权限还没有加载,延迟一下再尝试
|
||||
setTimeout(loadUserMenu, 500)
|
||||
return
|
||||
}
|
||||
|
||||
const res = await getUserMenuTree()
|
||||
if (res.code === 200) {
|
||||
userMenuTree.value = res.data || []
|
||||
console.log('用户菜单树:', userMenuTree.value)
|
||||
console.log('用户权限列表:', permissions)
|
||||
console.log('过滤后的菜单:', filteredMenuTree.value)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户菜单失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听权限变化,重新加载菜单
|
||||
watch(() => getUserPermissions(), (newPermissions, oldPermissions) => {
|
||||
console.log('权限变化检测:', { old: oldPermissions?.length || 0, new: newPermissions?.length || 0 })
|
||||
if (newPermissions.length > 0 && (oldPermissions?.length || 0) === 0) {
|
||||
console.log('权限首次加载,重新获取菜单')
|
||||
loadUserMenu()
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(() => {
|
||||
loadUserMenu()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-menu {
|
||||
height: 100%;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.el-menu-item.is-active {
|
||||
background-color: #263445 !important;
|
||||
}
|
||||
|
||||
:deep(.el-menu) {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
:deep(.el-sub-menu.is-opened) {
|
||||
|
||||
>.el-sub-menu__title,
|
||||
.el-menu-item {
|
||||
background-color: #1a1a1a !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-menu-item):hover,
|
||||
:deep(.el-sub-menu__title):hover {
|
||||
background-color: #1a1a1a !important;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item.is-active) {
|
||||
background-color: #1a1a1a !important;
|
||||
color: var(--el-menu-active-color);
|
||||
}
|
||||
|
||||
:deep(.el-menu-item.is-active + .el-sub-menu .el-sub-menu__title),
|
||||
:deep(.el-menu-item.is-active)~.el-sub-menu .el-sub-menu__title {
|
||||
background-color: #1a1a1a !important;
|
||||
}
|
||||
|
||||
:deep(.el-sub-menu) {
|
||||
&.is-active {
|
||||
>.el-sub-menu__title {
|
||||
background-color: #1a1a1a !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
48
src/utils/permission.js
Normal file
48
src/utils/permission.js
Normal file
@ -0,0 +1,48 @@
|
||||
import { getUserPermissionCodes } from '@/api/permission'
|
||||
|
||||
let userPermissions = []
|
||||
|
||||
/**
|
||||
* 初始化用户权限
|
||||
*/
|
||||
export async function initUserPermissions() {
|
||||
try {
|
||||
const res = await getUserPermissionCodes()
|
||||
if (res.code === 200) {
|
||||
userPermissions = res.data.filter(code => code && code.trim() !== '')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户权限失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否有指定权限
|
||||
*/
|
||||
export function hasPermission(permissionCode) {
|
||||
if (!permissionCode) return true
|
||||
return userPermissions.includes(permissionCode)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否有任意一个权限
|
||||
*/
|
||||
export function hasAnyPermission(permissionCodes) {
|
||||
if (!permissionCodes || permissionCodes.length === 0) return true
|
||||
return permissionCodes.some(code => hasPermission(code))
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否有所有权限
|
||||
*/
|
||||
export function hasAllPermissions(permissionCodes) {
|
||||
if (!permissionCodes || permissionCodes.length === 0) return true
|
||||
return permissionCodes.every(code => hasPermission(code))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户所有权限
|
||||
*/
|
||||
export function getUserPermissions() {
|
||||
return userPermissions
|
||||
}
|
||||
157
src/views/Monitor/Dashboard.vue
Normal file
157
src/views/Monitor/Dashboard.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div class="monitor-dashboard-wrapper">
|
||||
<!-- 嵌入Go监控系统的原生页面 -->
|
||||
<iframe ref="monitorFrame" :src="monitorUrl" class="monitor-iframe" frameborder="0"
|
||||
@load="onFrameLoad"></iframe>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading" class="loading-overlay">
|
||||
<div class="loading-spinner">
|
||||
<i class="el-icon-loading"></i>
|
||||
</div>
|
||||
<p>正在加载监控大屏...</p>
|
||||
</div>
|
||||
|
||||
<!-- 错误状态 -->
|
||||
<div v-if="error" class="error-overlay">
|
||||
<el-icon>
|
||||
<Warning />
|
||||
</el-icon>
|
||||
<p>{{ error }}</p>
|
||||
<el-button @click="reload" type="primary">重新加载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { Warning } from '@element-plus/icons-vue'
|
||||
|
||||
export default {
|
||||
name: 'MonitorDashboard',
|
||||
components: {
|
||||
Warning
|
||||
},
|
||||
setup() {
|
||||
const monitorFrame = ref(null)
|
||||
const loading = ref(true)
|
||||
const error = ref('')
|
||||
const monitorUrl = ref('http://118.195.145.61') // Go监控服务地址
|
||||
|
||||
// 检查监控服务是否可用
|
||||
const checkMonitorService = async () => {
|
||||
try {
|
||||
const response = await fetch(`${monitorUrl.value}/api/v1/health`)
|
||||
if (!response.ok) {
|
||||
throw new Error('监控服务不可用')
|
||||
}
|
||||
return true
|
||||
} catch (err) {
|
||||
error.value = '无法连接到监控服务,请确保Go监控服务已启动'
|
||||
loading.value = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const onFrameLoad = () => {
|
||||
loading.value = false
|
||||
error.value = ''
|
||||
}
|
||||
|
||||
const reload = () => {
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
if (monitorFrame.value) {
|
||||
monitorFrame.value.src = monitorUrl.value
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 检查监控服务
|
||||
const isAvailable = await checkMonitorService()
|
||||
if (!isAvailable) {
|
||||
return
|
||||
}
|
||||
|
||||
// 设置iframe源
|
||||
if (monitorFrame.value) {
|
||||
monitorFrame.value.src = monitorUrl.value
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
monitorFrame,
|
||||
monitorUrl,
|
||||
loading,
|
||||
error,
|
||||
onFrameLoad,
|
||||
reload
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.monitor-dashboard-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monitor-iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.loading-overlay,
|
||||
.error-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.loading-overlay p,
|
||||
.error-overlay p {
|
||||
margin-top: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.error-overlay .el-icon {
|
||||
font-size: 48px;
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.error-overlay .el-button {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
font-size: 32px;
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
|
||||
.loading-spinner i {
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
968
src/views/logistics/index.vue
Normal file
968
src/views/logistics/index.vue
Normal file
@ -0,0 +1,968 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<div v-show="showSearch" class="mb-10">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="物流模板名称" prop="templateName" label-width="100">
|
||||
<el-input v-model="queryParams.templateName" placeholder="请输入物流模板名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="详细地址" prop="deliveryAddress" label-width="100">
|
||||
<el-input v-model="queryParams.deliveryAddress" placeholder="请输入详细地址" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="运送方式" prop="shipping">
|
||||
<el-input v-model="queryParams.shipping" placeholder="请输入运送方式" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
|
||||
</el-col>
|
||||
<!-- <el-col :span="1.5">-->
|
||||
<!-- <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zhishu:logistics:export']">导出</el-button>-->
|
||||
<!-- </el-col>-->
|
||||
<el-button icon="Search" @click="showSearch = !showSearch">{{ showSearch ? '隐藏搜索' : '显示搜索' }}</el-button>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" :data="logisticsList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="" align="center" prop="id" v-if="false" />
|
||||
<el-table-column label="物流模板名称" align="center" prop="templateName" />
|
||||
<el-table-column label="详细地址" align="center" prop="deliveryAddress">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.deliveryAddress!=null">{{row.deliveryAddress}}</span>
|
||||
<span v-else></span>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="计价方式" align="center" prop="pricingMethod">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.pricingMethod==0">按重量</span>
|
||||
<span v-else-if="row.pricingMethod==1">按标准本数(图书专用)</span>
|
||||
<span v-else-if="row.pricingMethod==2">按件数</span>
|
||||
<span v-else-if="row.pricingMethod==3">单独设置运费</span>
|
||||
<span v-else></span>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="运送方式" align="center" prop="shipping">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.shipping==0">快递</span>
|
||||
<span v-else-if="row.shipping==1">物流</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="模板状态" align="center" prop="status">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === '0' ? 'success' : 'danger'" :effect="row.status === '0' ? 'dark' : 'light'"
|
||||
size="large">
|
||||
{{ row.status === '0' ? '正常' : '异常(请先修改模版内容)' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- <el-table-column label="仓库名称" align="center" prop="warehouseName" />-->
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改运费模版" placement="top">
|
||||
<el-button link type="primary" icon="Document" @click="handleFreightTemplate(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
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"
|
||||
@size-change="getList"
|
||||
@current-change="getList"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 添加或修改物流管理对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="logisticsFormRef" :model="form" :rules="Temrules" label-width="80px">
|
||||
<!-- <el-form-item label="货区名称" prop="depotId" >-->
|
||||
<!-- <el-select v-model="selectedId" value-key="id" placeholder="请选择一级货区" :reserve-keyword="false" clearable filterable style="width: 100%" :loading="loading" @update:model-value="handleDepotChange">-->
|
||||
<!-- <el-option v-for="item in depotList" :key="item.id" :label="item.name+''+item.unit" :value="item" />-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="模板名称" prop="templateName">
|
||||
<el-input v-model="form.templateName" placeholder="请输入物流模板名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="发货地" prop="deliveryArea">
|
||||
<el-cascader v-model="freightForm.deliveryArea" :props="cascaderProps" placeholder="请选择发货地" clearable
|
||||
style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 运费模板对话框 -->
|
||||
<el-dialog title="运费模板设置" v-model="freightDialog.visible" width="1000px" append-to-body>
|
||||
<div class="freight-template-container">
|
||||
<el-form :model="freightForm" ref="templatesFrom" label-width="80px" :rules="freightRule" >
|
||||
<el-form-item label="模板名称">
|
||||
<el-input v-model="freightForm.templateName" placeholder="请输入模板名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="发货地">
|
||||
<el-cascader v-model="freightForm.deliveryArea" :props="cascaderProps" placeholder="请选择发货地" clearable
|
||||
style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="默认联系人" label-width="100" prop="contact">
|
||||
<el-input v-model="freightForm.contact" placeholder="请输入联系人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="手机号" prop="phoneNumber">
|
||||
<el-input v-model="freightForm.phoneNumber" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="详细地址" prop="fullAddress">
|
||||
<el-input v-model="freightForm.fullAddress" placeholder="请输入详细地址" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="计价方式">
|
||||
<el-radio-group v-model="freightForm.pricingMethod">
|
||||
<el-radio :value="'weight'" border size="large">按重量</el-radio>
|
||||
<el-radio :value="'book'" border size="large">按标准本数(图书专用)</el-radio>
|
||||
<el-radio :value="'piece'" border size="large">按件数</el-radio>
|
||||
<el-radio :value="'custom'" border size="large">单独设置运费</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="运送方式">
|
||||
<el-select v-model="freightForm.deliveryMethod" placeholder="请选择运送方式">
|
||||
<el-option label="快递" value="express" />
|
||||
<el-option label="物流" value="logistics" />
|
||||
</el-select>
|
||||
<el-button type="danger" plain size="small" style="margin-left: 10px"
|
||||
@click="deleteDeliveryMethod">删除</el-button>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="运送范围">
|
||||
<el-table :data="deliveryRanges" style="width: 100%" border>
|
||||
<el-table-column label="运送范围" width="200">
|
||||
<template #default="{ row }">
|
||||
{{ row.region }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 动态列 -->
|
||||
<template v-for="column in dynamicColumns" :key="column.prop">
|
||||
<el-table-column :label="column.label" :width="column.width">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row[column.prop]"
|
||||
type="number"
|
||||
:placeholder="column.placeholder"
|
||||
@blur="column.validate ? validateFee(row[column.prop]) : null"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" type="primary" link @click="editRegion(row)">编辑地区</el-button>
|
||||
<el-button size="small" type="danger" link @click="deleteRegion(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="配送说明">
|
||||
<el-input v-model="freightForm.deliveryNote" type="textarea" :rows="3"
|
||||
placeholder="请输入配送说明,输入的内容将会在商品详情页面展示,不超过500个字" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="saveFreightTemplate">确定</el-button>
|
||||
<el-button @click="cancelFreightTemplate">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 区域编辑对话框 -->
|
||||
<el-dialog :title="regionDialog.title" v-model="regionDialog.visible" width="600px" append-to-body>
|
||||
<div class="region-edit-container">
|
||||
<el-checkbox-group v-model="selectedProvinces">
|
||||
<el-checkbox v-for="province in allProvinces" :key="province" :value="province" :disabled="deliveryRanges.some(item => item.region !== regionDialog.currentRegion && item.region.includes(province))">
|
||||
{{ province }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSaveRegion">确 定</el-button>
|
||||
<el-button @click="regionDialog.visible = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup name="Logistics">
|
||||
import { ref, reactive, computed, onMounted, toRefs } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
// 导入API模块
|
||||
import {
|
||||
listLogistics,
|
||||
getLogistics,
|
||||
delLogistics,
|
||||
addLogistics,
|
||||
updateLogistics,
|
||||
} from '@/api/modules/logistics'
|
||||
import {
|
||||
getCitiesByProvinceId,
|
||||
getDistrictsByCityId,
|
||||
getFreInfo,
|
||||
getProvinces,
|
||||
UpdateTemplate
|
||||
} from '@/api/modules/district'
|
||||
// import { depotNameList } from '@/api/modules/shelves'
|
||||
|
||||
let curruntRow = null;
|
||||
|
||||
let isValid = true; // 全局校验标志
|
||||
const queryFormRef = ref();
|
||||
const logisticsFormRef = ref();
|
||||
const templatesFrom = ref();
|
||||
// 运费模板对话框状态
|
||||
const freightDialog = reactive({
|
||||
visible: false,
|
||||
title: '运费模板设置'
|
||||
});
|
||||
// 运费模板表单数据
|
||||
const freightForm = reactive({
|
||||
templateName: '',
|
||||
contact: '',
|
||||
phoneNumber: '',
|
||||
fullAddress: '',
|
||||
deliveryArea: [],
|
||||
pricingMethod: '',
|
||||
deliveryMethod: 'express',
|
||||
deliveryNote: ''
|
||||
});
|
||||
|
||||
const validateFee = async (row) => {
|
||||
if(row === null || row === ''){
|
||||
isValid = false;
|
||||
ElMessage.error(' 首费/运费不能为空');
|
||||
} else {
|
||||
isValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认的运送范围
|
||||
const defaultDeliveryRanges = ref([
|
||||
{
|
||||
region: '河北',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
cities: ['保定', '沧州', '承德', '邯郸', '衡水', '廊坊', '秦皇岛', '石家庄', '唐山', '邢台', '张家口'], // 写死的市级名称
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '北京',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '天津',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '重庆',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '上海、江苏、浙江、安徽、江西、山西、山东、内蒙古、湖南、湖北、河南、广东、广西、福建、海南、辽宁、吉林、黑龙江、陕西、云南、贵州、四川',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '甘肃、宁夏、青海',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '新疆、西藏',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '香港、澳门、台湾、海外',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
}
|
||||
]);
|
||||
// 模拟运送范围数据
|
||||
const deliveryRanges =ref([
|
||||
{
|
||||
region: '河北',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
cities: ['保定', '沧州', '承德', '邯郸', '衡水', '廊坊', '秦皇岛', '石家庄', '唐山', '邢台', '张家口'], // 写死的市级名称
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '北京',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '天津',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '重庆',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '上海、江苏、浙江、安徽、江西、山西、山东、内蒙古、湖南、湖北、河南、广东、广西、福建、海南、辽宁、吉林、黑龙江、陕西、云南、贵州、四川',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '甘肃、宁夏、青海',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '新疆、西藏',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
},
|
||||
{
|
||||
region: '香港、澳门、台湾、海外',
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
}
|
||||
]);
|
||||
// @blur="validateFee(row.firstFee)"
|
||||
|
||||
// 根据计价方式展示不同内容
|
||||
const dynamicColumns = computed(() => {
|
||||
const method = freightForm.pricingMethod;
|
||||
|
||||
if (method === 'weight') {
|
||||
return [
|
||||
{ prop: 'firstWeight', label: '首重(千克)', placeholder: '请输入首重', width: '120' },
|
||||
{ prop: 'firstFee', label: '首费(元)', placeholder: '请输入首费', width: '120',validate: true },
|
||||
{ prop: 'continueWeight', label: '续重(千克)', placeholder: '请输入续重', width: '120' },
|
||||
{ prop: 'continueFee', label: '续费(元)', placeholder: '请输入续费', width: '120' },
|
||||
];
|
||||
} else if (method === 'book') {
|
||||
return [
|
||||
{ prop: 'firstWeight', label: '首重本数(本)', placeholder: '请输入首本', width: '120' },
|
||||
{ prop: 'firstFee', label: '首费(元)', placeholder: '请输入首费', width: '120',validate: true },
|
||||
{ prop: 'continueWeight', label: '续重本数(本)', placeholder: '请输入续本', width: '120' },
|
||||
{ prop: 'continueFee', label: '续费(元)', placeholder: '请输入续费', width: '120' },
|
||||
];
|
||||
} else if (method === 'piece') {
|
||||
return [
|
||||
{ prop: 'firstWeight', label: '首件数(件)', placeholder: '请输入首件', width: '120' },
|
||||
{ prop: 'firstFee', label: '首费(元)', placeholder: '请输入首费', width: '120',validate: true },
|
||||
{ prop: 'continueWeight', label: '续件数(件)', placeholder: '请输入续件', width: '120' },
|
||||
{ prop: 'continueFee', label: '续费(元)', placeholder: '请输入续费', width: '120' },
|
||||
];
|
||||
} else if(method === 'custom'){
|
||||
return [
|
||||
{ prop: 'firstFee', label: '运费(元)', placeholder: '请输入运费', width: '120',validate: true },
|
||||
|
||||
];
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
const handleFreightTemplate = async (row) => {
|
||||
freightDialog.visible = true;
|
||||
// 加载省级数据
|
||||
// 可以根据仓库信息加载对应的运费模板数据
|
||||
const res = await getFreInfo(row.id);
|
||||
console.log(row.id);
|
||||
freightForm.templateName=row.templateName;
|
||||
const { deliveryProvince: pro, deliveryCity: city, deliveryArea: area } = res;
|
||||
freightForm.deliveryArea=[Number(pro),Number(city),Number(area)]
|
||||
// 数字编码 转换 对应的计价方式
|
||||
const pricingMethodMap = {
|
||||
0:'weight',
|
||||
1:'book',
|
||||
2:'piece',
|
||||
3:'custom'
|
||||
};
|
||||
freightForm.pricingMethod= pricingMethodMap[row.pricingMethod];
|
||||
// 数字编码对应运送方式
|
||||
const deliveryMethodMap = {
|
||||
0:'express',
|
||||
1:'logistics'
|
||||
};
|
||||
freightForm.deliveryMethod=deliveryMethodMap[row.shipping]
|
||||
// dataRange=row.shippingRange;
|
||||
if(row.shippingRange!=null){
|
||||
const parsedData=JSON.parse(row.shippingRange);
|
||||
deliveryRanges.value = transformData(parsedData)
|
||||
|
||||
}else{
|
||||
// 使用深拷贝创建全新的默认值
|
||||
deliveryRanges.value = JSON.parse(JSON.stringify(defaultDeliveryRanges.value))
|
||||
}
|
||||
freightForm.contact=res.contact;
|
||||
freightForm.phoneNumber=res.phoneNumber;
|
||||
freightForm.fullAddress=res.fullAddress;
|
||||
freightForm.deliveryNote=res.remark;
|
||||
curruntRow = row;
|
||||
console.log(freightForm)
|
||||
};
|
||||
|
||||
const transformData= (dataRange) => {
|
||||
const result=[];
|
||||
for (const region in dataRange) {
|
||||
const [firstWeight, firstFee, continueWeight, continueFee] = dataRange[region];
|
||||
result.push({
|
||||
region,
|
||||
firstWeight: parseFloat(firstWeight) || 0.0,
|
||||
firstFee: parseFloat(firstFee)||0.0,
|
||||
continueWeight: parseFloat(continueWeight) || 0.0,
|
||||
continueFee: parseFloat(continueFee)||0.0
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const freightRule= reactive({
|
||||
contact: [
|
||||
{ required: true, message: '请输入默认联系人', trigger: 'blur' }
|
||||
],
|
||||
phoneNumber: [
|
||||
{ required: true, message: '请输入联系电话', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||
],
|
||||
fullAddress: [
|
||||
{ required: true, message: '请输入详细地址', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
/** 删除运送方式 */
|
||||
const deleteDeliveryMethod = async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除该运送方式吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
ElMessage.success('删除成功');
|
||||
// 实际应用中应调用API删除数据
|
||||
} catch (error) {
|
||||
console.log('取消删除');
|
||||
}
|
||||
};
|
||||
|
||||
const allProvinces = ['北京', '天津', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江', '上海', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南', '湖北', '湖南', '广东', '广西', '海南', '重庆', '四川', '贵州', '云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆', '香港', '澳门', '台湾'];
|
||||
|
||||
|
||||
|
||||
// 区域编辑对话框状态
|
||||
const regionDialog = reactive({
|
||||
visible: false,
|
||||
title: '编辑地区',
|
||||
currentRegion: ''
|
||||
});
|
||||
// 当前选中的省份列表
|
||||
const selectedProvinces = ref([]);
|
||||
const editRegion = (row) => {
|
||||
regionDialog.visible = true;
|
||||
regionDialog.currentRegion = row.region;
|
||||
// 将当前行的地区字符串转换为数组
|
||||
selectedProvinces.value = row.region.split('、');
|
||||
};
|
||||
/** 删除地区 */
|
||||
const deleteRegion = async (row) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除该地区吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
const index = deliveryRanges.value.findIndex(item => item.region === row.region);
|
||||
if (index !== -1) {
|
||||
deliveryRanges.value.splice(index, 1);
|
||||
ElMessage.success('删除成功');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('取消删除');
|
||||
}
|
||||
};
|
||||
/** 保存运费模板 */
|
||||
const saveFreightTemplate = async () => {
|
||||
if (!templatesFrom.value) return;
|
||||
|
||||
try {
|
||||
const valid = await templatesFrom.value.validate();
|
||||
if(valid) {
|
||||
buttonLoading.value = true;
|
||||
|
||||
// 获取选择的地区信息(省、市、区)
|
||||
const [provinceId, cityId, districtId] = freightForm.deliveryArea;
|
||||
|
||||
// 转换计价方式为数字编码
|
||||
const pricingMethodMap = {
|
||||
'weight': 0,
|
||||
'book': 1,
|
||||
'piece': 2,
|
||||
'custom': 3
|
||||
};
|
||||
// 转换运送方式为数字编码
|
||||
const deliveryMethodMap = {
|
||||
'express': 0,
|
||||
'logistics': 1
|
||||
};
|
||||
|
||||
const shippingRanges = deliveryRanges.value.reduce((acc, range) => {
|
||||
acc[range.region] = [range.firstWeight, range.firstFee, range.continueWeight, range.continueFee];
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// 收集所有数据
|
||||
const templateData = {
|
||||
id: curruntRow.id,
|
||||
template_name: freightForm.templateName,
|
||||
contact: freightForm.contact,
|
||||
phoneNumber: freightForm.phoneNumber,
|
||||
fullAddress: freightForm.fullAddress,
|
||||
delivery_province: provinceId,
|
||||
delivery_city: cityId,
|
||||
delivery_area: districtId,
|
||||
pricing_method: pricingMethodMap[freightForm.pricingMethod],
|
||||
shipping: deliveryMethodMap[freightForm.deliveryMethod],
|
||||
shipping_range: shippingRanges,
|
||||
warehouse_id: curruntRow.id,
|
||||
remark: freightForm.deliveryNote
|
||||
};
|
||||
|
||||
if (isValid) {
|
||||
await UpdateTemplate(templateData);
|
||||
ElMessage.success('运费模板保存成功');
|
||||
freightDialog.visible = false;
|
||||
} else {
|
||||
ElMessage.error('首费/运费不能为空');
|
||||
}
|
||||
|
||||
buttonLoading.value = false;
|
||||
getList();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('表单验证或保存失败:', error);
|
||||
ElMessage.error('操作失败,请检查输入信息');
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 取消运费模板 */
|
||||
const cancelFreightTemplate = () => {
|
||||
freightDialog.visible = false;
|
||||
};
|
||||
/** 保存区域设置 */
|
||||
const handleSaveRegion = () => {
|
||||
if (selectedProvinces.value.length === 0) {
|
||||
ElMessage.error('请至少选择一个省份');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取未选中的省份
|
||||
const unselectedProvinces = getUnselectedProvinces();
|
||||
// 更新deliveryRanges中对应行的region数据
|
||||
const index = deliveryRanges.value.findIndex(item => item.region === regionDialog.currentRegion);
|
||||
if (index !== -1) {
|
||||
// 更新当前选中的省份
|
||||
deliveryRanges.value[index].region = selectedProvinces.value.join('、');
|
||||
// 如果有取消勾选的省份,添加到最后一行或更新现有的取消勾选省份行
|
||||
if (unselectedProvinces.length > 0) {
|
||||
// 查找是否已存在取消勾选的省份行
|
||||
const unselectedIndex = deliveryRanges.value.findIndex(item =>
|
||||
// 检查是否包含取消勾选的任一省份
|
||||
unselectedProvinces.some(province => item.region.includes(province)));
|
||||
|
||||
if (unselectedIndex !== -1 && unselectedIndex !== index) {
|
||||
// 如果已存在包含这些省份的行,则更新该行
|
||||
deliveryRanges.value[unselectedIndex].region = unselectedProvinces.join('、');
|
||||
} else {
|
||||
// 否则添加新行
|
||||
deliveryRanges.value.push({
|
||||
region: unselectedProvinces.join('、'),
|
||||
firstWeight: 1.0,
|
||||
firstFee: 1.0,
|
||||
continueWeight: 1.0,
|
||||
continueFee: 1.0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
regionDialog.visible = false;
|
||||
};
|
||||
// 级联选择器配置
|
||||
const cascaderProps = reactive({
|
||||
lazy: true,
|
||||
async lazyLoad(node, resolve) {
|
||||
const { level, value: parentId } = node;
|
||||
try {
|
||||
let nodes = [];
|
||||
|
||||
if (level === 0) { // 加载省份
|
||||
const res = await getProvinces();
|
||||
nodes = res.data.map(formatNode(0));
|
||||
}
|
||||
else if (level === 1) { // 加载城市
|
||||
const res = await getCitiesByProvinceId(parentId);
|
||||
nodes = res.data.map(formatNode(1));
|
||||
}
|
||||
else if (level === 2) { // 加载区县
|
||||
const res = await getDistrictsByCityId(parentId);
|
||||
nodes = res.data.map(formatNode(2, true));
|
||||
}
|
||||
|
||||
resolve(nodes);
|
||||
} catch (error) {
|
||||
resolve([]);
|
||||
}
|
||||
}
|
||||
});
|
||||
const formatNode = (level, isLeaf = false) => (item) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
leaf: isLeaf,
|
||||
level: level
|
||||
});
|
||||
// 获取本次编辑中取消勾选的省份
|
||||
const getUnselectedProvinces = () => {
|
||||
// 获取编辑前的省份列表
|
||||
const originalProvinces = regionDialog.currentRegion.split('、');
|
||||
// 获取当前选中的省份集合
|
||||
const selectedProvincesSet = new Set(selectedProvinces.value);
|
||||
// 只返回那些在原始列表中存在但在当前选择中不存在的省份(即用户取消勾选的省份)
|
||||
return originalProvinces.filter(province => !selectedProvincesSet.has(province));
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const logisticsList = ref([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
|
||||
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData = {
|
||||
id: undefined,
|
||||
templateName: undefined,
|
||||
deliveryProvince: undefined,
|
||||
deliveryCity: undefined,
|
||||
deliveryArea: undefined,
|
||||
deliveryAddress: undefined,
|
||||
pricingMethod: undefined,
|
||||
shipping: undefined,
|
||||
firWbv: undefined,
|
||||
firPrice: undefined,
|
||||
continueWbv: undefined,
|
||||
continuePrice: undefined,
|
||||
status: undefined,
|
||||
shippingRange: undefined,
|
||||
warehouseId: undefined,
|
||||
warehouseName: undefined
|
||||
}
|
||||
const data = reactive({
|
||||
form: {...initFormData},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
id:undefined,
|
||||
templateName: undefined,
|
||||
deliveryProvince: undefined,
|
||||
deliveryCity: undefined,
|
||||
deliveryArea: undefined,
|
||||
deliveryAddress: undefined,
|
||||
pricingMethod: undefined,
|
||||
shipping: undefined,
|
||||
firWbv: undefined,
|
||||
firPrice: undefined,
|
||||
continueWbv: undefined,
|
||||
continuePrice: undefined,
|
||||
status: undefined,
|
||||
shippingRange: undefined,
|
||||
warehouseId: undefined,
|
||||
warehouseName:undefined,
|
||||
params: {
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form } = toRefs(data);
|
||||
|
||||
// 新增中选择一级货区
|
||||
const Temrules = reactive({
|
||||
templateName: [
|
||||
{
|
||||
required: true,
|
||||
message: '模版名称不能为空',
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
// 发货地址校验
|
||||
deliveryArea: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择发货地',
|
||||
trigger: 'change', // 触发时机
|
||||
validator: (_, __, callback) => {
|
||||
const message = freightForm.deliveryArea;
|
||||
if(message.length == 0){
|
||||
callback(new Error('请选择完整地址'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// 这些变量和函数暂时未使用,可以在需要时添加
|
||||
// const selectedId = ref(null)
|
||||
// const depotList = ref([])
|
||||
// const loadData = async () => {
|
||||
// loading.value = true
|
||||
// try {
|
||||
// const res = await depotNameList()
|
||||
// depotList.value = res.rows
|
||||
// } catch (error) {
|
||||
// console.error('加载数据失败:', error)
|
||||
// } finally {
|
||||
// loading.value = false
|
||||
// }
|
||||
// }
|
||||
|
||||
// const handleDepotChange = (val) => {
|
||||
// form.value.depotId = val?.id || null
|
||||
// form.value.depotName = val?.name || ''
|
||||
// }
|
||||
|
||||
/** 查询物流管理列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listLogistics(queryParams.value);
|
||||
logisticsList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
}
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = {...initFormData};
|
||||
logisticsFormRef.value?.resetFields();
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
freightForm.deliveryArea = [];
|
||||
freightForm.pricingMethod = '2'
|
||||
dialog.visible = true;
|
||||
dialog.title = "添加物流管理";
|
||||
}
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0]
|
||||
const res = await getLogistics(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = "修改物流管理";
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
logisticsFormRef.value?.validate(async (valid) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
if (form.value.id) {
|
||||
await updateLogistics(form.value).finally(() => buttonLoading.value = false);
|
||||
} else {
|
||||
const [provinceId, cityId, districtId] = freightForm.deliveryArea;
|
||||
form.value.deliveryProvince = provinceId;
|
||||
form.value.deliveryCity = cityId;
|
||||
form.value.deliveryArea = districtId;
|
||||
form.value.pricingMethod = 1;
|
||||
await addLogistics(form.value).finally(() => buttonLoading.value = false);
|
||||
}
|
||||
ElMessage.success("操作成功");
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
try {
|
||||
await ElMessageBox.confirm('是否确认删除物流管理编号为"' + _ids + '"的数据项?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
await delLogistics(_ids);
|
||||
ElMessage.success("删除成功");
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.log('取消删除')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
// loadData(); // 暂时注释掉,因为相关函数被注释了
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.freight-template-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.region-edit-container {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.region-edit-container .el-checkbox {
|
||||
display: block;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.p-2 {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
560
src/views/redirectUrl/index.vue
Normal file
560
src/views/redirectUrl/index.vue
Normal file
@ -0,0 +1,560 @@
|
||||
<template>
|
||||
<div class="register-page">
|
||||
<div class="register-container">
|
||||
<div class="register-form">
|
||||
<!-- 标题 -->
|
||||
<div class="form-header">
|
||||
<h2>与书同行</h2>
|
||||
<!-- <div class="language-switch">
|
||||
<span>A</span>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<!-- 表单 -->
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="0">
|
||||
<!-- 用户名 -->
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="formData.username"
|
||||
placeholder="用户名"
|
||||
size="large"
|
||||
:prefix-icon="User"
|
||||
disabled
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 手机号 -->
|
||||
<el-form-item prop="phoneNumber">
|
||||
<el-input
|
||||
v-model="formData.phoneNumber"
|
||||
placeholder="手机号"
|
||||
size="large"
|
||||
:prefix-icon="Phone"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 密码 -->
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="formData.password"
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
size="large"
|
||||
:prefix-icon="Lock"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 确认密码 -->
|
||||
<el-form-item prop="confirmPassword">
|
||||
<el-input
|
||||
v-model="formData.confirmPassword"
|
||||
type="password"
|
||||
placeholder="确认密码"
|
||||
size="large"
|
||||
:prefix-icon="Lock"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 邀请码 -->
|
||||
<el-form-item prop="inviteCode">
|
||||
<el-input
|
||||
v-model="formData.inviteCode"
|
||||
placeholder="邀请码(非必填)"
|
||||
size="large"
|
||||
:prefix-icon="Message"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 验证码 -->
|
||||
<el-form-item v-if="captchaEnabled" prop="code">
|
||||
<div class="captcha-row">
|
||||
<el-input
|
||||
v-model="formData.code"
|
||||
placeholder="验证码"
|
||||
size="large"
|
||||
:prefix-icon="Key"
|
||||
/>
|
||||
<div class="captcha-image" @click="getCaptcha">
|
||||
<img v-if="captchaImg" :src="captchaImg" alt="验证码" />
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 注册按钮 -->
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="large"
|
||||
class="register-btn"
|
||||
@click="handleRegister"
|
||||
:loading="loading"
|
||||
>
|
||||
注册
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { User, Phone, Lock, Message, Key } from '@element-plus/icons-vue'
|
||||
import { getApiUrl } from '@/config/api'
|
||||
|
||||
const route = useRoute()
|
||||
const loading = ref(false)
|
||||
const formRef = ref()
|
||||
const captchaImg = ref('')
|
||||
const captchaEnabled = ref(true)
|
||||
|
||||
// URL参数
|
||||
const urlParams = ref({
|
||||
pddMallId: '',
|
||||
pddMallName: '',
|
||||
type: '',
|
||||
accessToken: '',
|
||||
skuSpec: ''
|
||||
})
|
||||
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
username: '',
|
||||
phoneNumber: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
inviteCode: '',
|
||||
code: '',
|
||||
uuid: '',
|
||||
clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
grantType: 'password',
|
||||
tenantId: '000000',
|
||||
userType: 'sys_user'
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '用户名长度在2到20个字符', trigger: 'blur' }
|
||||
],
|
||||
phoneNumber: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 5, max: 20, message: '密码长度在5到20个字符', trigger: 'blur' }
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '请确认密码', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value !== formData.value.password) {
|
||||
callback(new Error('两次输入密码不一致'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
code: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入验证码',
|
||||
trigger: 'blur',
|
||||
validator: (rule, value, callback) => {
|
||||
// 如果验证码未启用,直接通过验证
|
||||
if (!captchaEnabled.value) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
// 如果验证码启用但为空,则验证失败
|
||||
if (!value) {
|
||||
callback(new Error('请输入验证码'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 获取URL参数
|
||||
const getUrlParams = () => {
|
||||
const query = route.query
|
||||
urlParams.value = {
|
||||
pddMallId: query.pddMallId || '',
|
||||
pddMallName: decodeURIComponent(query.pddMallName || ''),
|
||||
type: query.type || '',
|
||||
accessToken: query.accessToken || '',
|
||||
skuSpec: decodeURIComponent(query.skuSpec || '')
|
||||
}
|
||||
|
||||
// 将 "ppd" + pddMallId 回填到用户名
|
||||
if (urlParams.value.pddMallId) {
|
||||
formData.value.username = 'pdd' + urlParams.value.pddMallId
|
||||
}
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
const getCaptcha = async () => {
|
||||
try {
|
||||
const response = await fetch(getApiUrl('/auth/code'), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
// 检查响应内容类型
|
||||
const contentType = response.headers.get('content-type')
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
const result = await response.json()
|
||||
if (result.code === 200) {
|
||||
const { data } = result
|
||||
|
||||
// 检查是否启用验证码
|
||||
captchaEnabled.value = data.captchaEnabled === undefined ? true : data.captchaEnabled
|
||||
|
||||
if (captchaEnabled.value) {
|
||||
// 设置验证码图片和UUID
|
||||
formData.value.uuid = data.uuid
|
||||
captchaImg.value = 'data:image/gif;base64,' + data.img
|
||||
} else {
|
||||
// 如果验证码未启用,清空相关字段
|
||||
formData.value.uuid = ''
|
||||
captchaImg.value = ''
|
||||
// 可以隐藏验证码输入框或显示提示
|
||||
}
|
||||
} else {
|
||||
// 处理验证码获取失败的错误信息
|
||||
let errorMessage = result.msg || '获取验证码失败'
|
||||
|
||||
// 将英文验证码错误转换为中文
|
||||
if (result.msg === 'Captcha error') {
|
||||
errorMessage = '验证码生成失败,请重试'
|
||||
} else if (result.msg === 'Captcha invalid') {
|
||||
errorMessage = '验证码服务异常,请重试'
|
||||
} else if (result.msg && (
|
||||
result.msg.includes('Captcha') ||
|
||||
result.msg.includes('captcha')
|
||||
)) {
|
||||
errorMessage = '验证码服务异常,请重试'
|
||||
}
|
||||
|
||||
ElMessage.error(errorMessage)
|
||||
}
|
||||
} else {
|
||||
// 服务器返回的不是JSON格式
|
||||
const text = await response.text()
|
||||
ElMessage.error('服务器响应格式错误,请检查API接口')
|
||||
}
|
||||
} else {
|
||||
// HTTP状态码不是200-299
|
||||
ElMessage.error(`获取验证码失败 (${response.status}),请重试`)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.name === 'SyntaxError' && error.message.includes('JSON')) {
|
||||
ElMessage.error('服务器响应格式错误,请检查API接口')
|
||||
} else {
|
||||
ElMessage.error('网络错误,获取验证码失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 注册处理
|
||||
const handleRegister = async () => {
|
||||
try {
|
||||
// 表单验证
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
loading.value = true
|
||||
|
||||
// 构建注册数据
|
||||
const registerData = {
|
||||
username: formData.value.username,
|
||||
password: formData.value.password,
|
||||
phoneNumber: formData.value.phoneNumber,
|
||||
inviteCode: formData.value.inviteCode,
|
||||
clientId: formData.value.clientId,
|
||||
grantType: formData.value.grantType,
|
||||
tenantId: formData.value.tenantId,
|
||||
userType: formData.value.userType,
|
||||
// 拼多多相关参数
|
||||
pddMallId: urlParams.value.pddMallId,
|
||||
pddMallName: urlParams.value.pddMallName,
|
||||
pddType: urlParams.value.type,
|
||||
accessToken: urlParams.value.accessToken,
|
||||
skuSpec: urlParams.value.skuSpec
|
||||
}
|
||||
|
||||
// 只在验证码启用时添加验证码相关字段
|
||||
if (captchaEnabled.value) {
|
||||
registerData.code = formData.value.code
|
||||
registerData.uuid = formData.value.uuid
|
||||
}
|
||||
|
||||
// 调用注册API
|
||||
const response = await fetch(getApiUrl('/auth/register'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(registerData)
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
if (result.code === 200) {
|
||||
// 显示注册成功弹窗
|
||||
showSuccessDialog()
|
||||
// 注册成功后可以跳转到登录页面或其他页面
|
||||
// router.push('/login')
|
||||
} else {
|
||||
// 处理验证码相关错误
|
||||
let errorMessage = result.msg || '注册失败,请重试'
|
||||
|
||||
// 将英文验证码错误转换为中文
|
||||
if (result.msg === 'Captcha error') {
|
||||
errorMessage = '验证码错误,请重新输入'
|
||||
} else if (result.msg === 'Captcha invalid') {
|
||||
errorMessage = '验证码无效,请重新输入'
|
||||
} else if (result.msg && (
|
||||
result.msg.includes('Captcha') ||
|
||||
result.msg.includes('captcha') ||
|
||||
result.msg.includes('验证码')
|
||||
)) {
|
||||
errorMessage = '验证码错误,请重新输入'
|
||||
}
|
||||
|
||||
ElMessage.error(errorMessage)
|
||||
|
||||
// 如果是验证码相关错误,自动刷新验证码
|
||||
if (captchaEnabled.value && result.msg && (
|
||||
result.msg === 'Captcha error' ||
|
||||
result.msg === 'Captcha invalid' ||
|
||||
result.msg.includes('Captcha') ||
|
||||
result.msg.includes('captcha') ||
|
||||
result.msg.includes('验证码')
|
||||
)) {
|
||||
// 自动刷新验证码
|
||||
getCaptcha()
|
||||
// 清空验证码输入框
|
||||
formData.value.code = ''
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('注册失败:', error)
|
||||
ElMessage.error('注册失败,请重试')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 显示注册成功弹窗
|
||||
const showSuccessDialog = () => {
|
||||
const loginUrl = 'https://erp.buzhiyushu.cn/'
|
||||
|
||||
ElMessageBox({
|
||||
title: '注册成功!',
|
||||
message: `
|
||||
<div style="text-align: center;">
|
||||
<p style="margin-bottom: 15px; color: #67C23A; font-size: 16px;">🎉 恭喜您注册成功!</p>
|
||||
<p style="margin-bottom: 15px; color: #606266;">请复制以下链接进行登录:</p>
|
||||
<div style="background: #f5f7fa; padding: 10px; border-radius: 4px; margin-bottom: 15px; word-break: break-all;">
|
||||
${loginUrl}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
dangerouslyUseHTMLString: true,
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '复制链接',
|
||||
cancelButtonText: '关闭',
|
||||
confirmButtonClass: 'el-button--primary',
|
||||
cancelButtonClass: 'el-button--default',
|
||||
center: true
|
||||
}).then(async () => {
|
||||
// 点击复制按钮
|
||||
try {
|
||||
await navigator.clipboard.writeText(loginUrl)
|
||||
ElMessage.success('链接已复制到剪贴板!')
|
||||
} catch (err) {
|
||||
// 如果复制失败,使用备用方法
|
||||
try {
|
||||
// 创建临时文本域进行复制
|
||||
const textArea = document.createElement('textarea')
|
||||
textArea.value = loginUrl
|
||||
document.body.appendChild(textArea)
|
||||
textArea.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(textArea)
|
||||
ElMessage.success('链接已复制到剪贴板!')
|
||||
} catch (fallbackErr) {
|
||||
ElMessage.error('复制失败,请手动复制链接')
|
||||
}
|
||||
}
|
||||
}).catch(() => {
|
||||
// 点击关闭按钮或按ESC键
|
||||
console.log('用户关闭了弹窗')
|
||||
})
|
||||
}
|
||||
|
||||
// 页面加载时获取URL参数和验证码
|
||||
onMounted(() => {
|
||||
getUrlParams()
|
||||
getCaptcha()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.register-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.register-container {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.register-form {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 40px 30px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.form-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.form-header h2 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.language-switch {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.language-switch span {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.el-input {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
:deep(.el-input__wrapper) {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 0 1px #dcdfe6;
|
||||
border: none;
|
||||
}
|
||||
|
||||
:deep(.el-input__wrapper:hover) {
|
||||
box-shadow: 0 0 0 1px #c0c4cc;
|
||||
}
|
||||
|
||||
:deep(.el-input__wrapper.is-focus) {
|
||||
box-shadow: 0 0 0 1px #409eff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.captcha-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.captcha-row .el-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.captcha-image {
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.captcha-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.register-btn {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
font-size: 16px;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.register-btn:hover {
|
||||
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%);
|
||||
}
|
||||
|
||||
.login-link {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.login-link span {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-link span:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
/* 移动端适配 */
|
||||
@media (max-width: 480px) {
|
||||
.register-form {
|
||||
padding: 30px 20px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.form-header h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user