1
This commit is contained in:
@@ -12,6 +12,7 @@ const RakutenDashboard = defineAsyncComponent(() => import('./components/rakuten
|
||||
const AmazonDashboard = defineAsyncComponent(() => import('./components/amazon/AmazonDashboard.vue'))
|
||||
const ZebraDashboard = defineAsyncComponent(() => import('./components/zebra/ZebraDashboard.vue'))
|
||||
const UpdateDialog = defineAsyncComponent(() => import('./components/common/UpdateDialog.vue'))
|
||||
const SettingsDialog = defineAsyncComponent(() => import('./components/common/SettingsDialog.vue'))
|
||||
|
||||
const dashboardsMap: Record<string, Component> = {
|
||||
rakuten: RakutenDashboard,
|
||||
@@ -48,6 +49,9 @@ const userPermissions = ref<string>('')
|
||||
// 更新对话框状态
|
||||
const showUpdateDialog = ref(false)
|
||||
|
||||
// 设置对话框状态
|
||||
const showSettingsDialog = ref(false)
|
||||
|
||||
// 菜单配置 - 复刻ERP客户端格式
|
||||
const menuConfig = [
|
||||
{key: 'rakuten', name: 'Rakuten', index: 'rakuten', icon: 'R'},
|
||||
@@ -69,8 +73,6 @@ function hasPermission(module: string) {
|
||||
if (!permissions) {
|
||||
return defaultModules.includes(module) // 没有权限信息时显示默认菜单
|
||||
}
|
||||
|
||||
// 简化权限检查:直接检查模块名是否在权限字符串中
|
||||
return permissions.includes(module)
|
||||
}
|
||||
|
||||
@@ -132,6 +134,7 @@ function handleMenuSelect(key: string) {
|
||||
async function handleLoginSuccess(data: { token: string; permissions?: string }) {
|
||||
isAuthenticated.value = true
|
||||
showAuthDialog.value = false
|
||||
showRegDialog.value = false // 确保注册对话框也关闭
|
||||
|
||||
try {
|
||||
// 保存token到本地数据库
|
||||
@@ -209,11 +212,6 @@ function showRegisterDialog() {
|
||||
showRegDialog.value = true
|
||||
}
|
||||
|
||||
function handleRegisterSuccess() {
|
||||
showRegDialog.value = false
|
||||
showAuthDialog.value = true
|
||||
}
|
||||
|
||||
function backToLogin() {
|
||||
showRegDialog.value = false
|
||||
showAuthDialog.value = true
|
||||
@@ -362,6 +360,10 @@ async function openDeviceManager() {
|
||||
await fetchDeviceData()
|
||||
}
|
||||
|
||||
function openSettings() {
|
||||
showSettingsDialog.value = true
|
||||
}
|
||||
|
||||
async function fetchDeviceData() {
|
||||
if (!currentUsername.value) {
|
||||
ElMessage({
|
||||
@@ -472,7 +474,8 @@ onUnmounted(() => {
|
||||
@go-forward="goForward"
|
||||
@reload="reloadPage"
|
||||
@user-click="handleUserClick"
|
||||
@open-device="openDeviceManager"/>
|
||||
@open-device="openDeviceManager"
|
||||
@open-settings="openSettings"/>
|
||||
<div class="content-body">
|
||||
<div
|
||||
class="dashboard-home"
|
||||
@@ -500,12 +503,15 @@ onUnmounted(() => {
|
||||
|
||||
<RegisterDialog
|
||||
v-model="showRegDialog"
|
||||
@register-success="handleRegisterSuccess"
|
||||
@login-success="handleLoginSuccess"
|
||||
@back-to-login="backToLogin"/>
|
||||
|
||||
<!-- 更新对话框 -->
|
||||
<UpdateDialog v-model="showUpdateDialog" />
|
||||
|
||||
<!-- 设置对话框 -->
|
||||
<SettingsDialog v-model="showSettingsDialog" />
|
||||
|
||||
<!-- 设备管理弹框 -->
|
||||
<el-dialog
|
||||
v-model="showDeviceDialog"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
export type HttpMethod = 'GET' | 'POST';
|
||||
|
||||
const BASE_CLIENT = 'http://localhost:8081'; // erp_client_sb
|
||||
const BASE_RUOYI = 'http://localhost:8080';
|
||||
const BASE_RUOYI = 'http://192.168.1.89:8080';
|
||||
|
||||
function resolveBase(path: string): string {
|
||||
// 走 ruoyi-admin 的路径:鉴权与版本、平台工具路由
|
||||
|
||||
@@ -53,7 +53,7 @@ export const zebraApi = {
|
||||
return http.delete<void>(`/tool/banma/accounts/${id}`);
|
||||
},
|
||||
|
||||
// 业务采集(仍走客户端微服务 8081)
|
||||
// 业务采集
|
||||
getShops(params?: { accountId?: number }) {
|
||||
return http.get<{ data?: { list?: Array<{ id: string; shopName: string }> } }>(
|
||||
'/api/banma/shops', params as unknown as Record<string, unknown>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { amazonApi } from '../../api/amazon'
|
||||
import { handlePlatformFileExport } from '../../utils/settings'
|
||||
|
||||
// 响应式状态
|
||||
const loading = ref(false) // 主加载状态
|
||||
@@ -196,11 +197,9 @@ async function exportToExcel() {
|
||||
html += '</table>'
|
||||
|
||||
const blob = new Blob([html], { type: 'application/vnd.ms-excel' })
|
||||
const link = document.createElement('a')
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = `Amazon产品数据_${new Date().toISOString().slice(0, 10)}.xls`
|
||||
link.click()
|
||||
URL.revokeObjectURL(link.href)
|
||||
const fileName = `Amazon产品数据_${new Date().toISOString().slice(0, 10)}.xls`
|
||||
|
||||
await handlePlatformFileExport('amazon', blob, fileName)
|
||||
|
||||
clearInterval(progressInterval)
|
||||
exportProgress.value = 100
|
||||
|
||||
@@ -10,7 +10,7 @@ interface Props {
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'registerSuccess'): void
|
||||
(e: 'loginSuccess', data: { token: string; user: any }): void
|
||||
(e: 'backToLogin'): void
|
||||
}
|
||||
|
||||
@@ -53,12 +53,25 @@ async function handleRegister() {
|
||||
|
||||
registerLoading.value = true
|
||||
try {
|
||||
const result = await authApi.register({
|
||||
// 1. 注册
|
||||
await authApi.register({
|
||||
username: registerForm.value.username,
|
||||
password: registerForm.value.password
|
||||
})
|
||||
ElMessage.success(result.message || '注册成功,请登录')
|
||||
emit('registerSuccess')
|
||||
|
||||
// 2. 注册成功后直接登录
|
||||
const loginData = await authApi.login({
|
||||
username: registerForm.value.username,
|
||||
password: registerForm.value.password
|
||||
})
|
||||
|
||||
emit('loginSuccess', {
|
||||
token: loginData.token,
|
||||
user: {
|
||||
username: loginData.username,
|
||||
permissions: loginData.permissions
|
||||
}
|
||||
})
|
||||
resetForm()
|
||||
} catch (err) {
|
||||
ElMessage.error((err as Error).message)
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
type PlatformKey = 'zebra' | 'shopee' | 'rakuten' | 'amazon'
|
||||
|
||||
const props = defineProps<{ modelValue: boolean; platform?: PlatformKey }>()
|
||||
const emit = defineEmits(['update:modelValue', 'add'])
|
||||
const emit = defineEmits(['update:modelValue', 'add', 'refresh'])
|
||||
const visible = computed({ get: () => props.modelValue, set: v => emit('update:modelValue', v) })
|
||||
const curPlatform = ref<PlatformKey>(props.platform || 'zebra')
|
||||
const PLATFORM_LABEL: Record<PlatformKey, string> = {
|
||||
@@ -23,6 +23,9 @@ async function load() {
|
||||
const list = (res as any)?.data ?? res
|
||||
accounts.value = Array.isArray(list) ? list : []
|
||||
}
|
||||
|
||||
// 暴露方法供父组件调用
|
||||
defineExpose({ load })
|
||||
onMounted(load)
|
||||
|
||||
function switchPlatform(p: PlatformKey) {
|
||||
@@ -38,11 +41,12 @@ function formatDate(a: any) {
|
||||
async function onDelete(a: any) {
|
||||
const id = a?.id
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定删除账号 “${a?.name || a?.username || id}” 吗?`, '提示', { type: 'warning' })
|
||||
await ElMessageBox.confirm(`确定删除账号 "${a?.name || a?.username || id}" 吗?`, '提示', { type: 'warning' })
|
||||
} catch { return }
|
||||
await zebraApi.removeAccount(id)
|
||||
ElMessage({ message: '删除成功', type: 'success' })
|
||||
await load()
|
||||
emit('refresh') // 通知外层组件刷新账号列表
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -106,11 +106,13 @@ type Stage = 'check' | 'downloading' | 'completed'
|
||||
const stage = ref<Stage>('check')
|
||||
const appName = ref('我了个电商')
|
||||
const version = ref('2.0.0')
|
||||
const prog = ref({ percentage: 0, current: '0 MB', total: '0 MB', speed: '' as string | undefined })
|
||||
const prog = ref({ percentage: 0, current: '0 MB', total: '0 MB', speed: '' })
|
||||
const info = ref({
|
||||
latestVersion: '2.4.8',
|
||||
downloadUrl: '',
|
||||
updateNotes: '• 优化了用户界面体验\n• 修复了已知问题\n• 提升了系统稳定性\n• 增加了新的功能模块\n• 优化了数据处理性能'
|
||||
updateNotes: '• 优化了用户界面体验\n• 修复了已知问题\n• 提升了系统稳定性\n• 增加了新的功能模块\n• 优化了数据处理性能',
|
||||
currentVersion: '',
|
||||
hasUpdate: false
|
||||
})
|
||||
|
||||
async function autoCheck() {
|
||||
@@ -143,26 +145,24 @@ async function autoCheck() {
|
||||
|
||||
async function start() {
|
||||
if (!info.value.downloadUrl) {
|
||||
ElMessage({ message: '下载链接不可用', type: 'error' })
|
||||
return
|
||||
ElMessage({ message: '下载链接不可用', type: 'error' });
|
||||
return;
|
||||
}
|
||||
|
||||
stage.value = 'downloading'
|
||||
prog.value = { percentage: 0, current: '0 MB', total: '0 MB', speed: '' }
|
||||
stage.value = 'downloading';
|
||||
prog.value = { percentage: 0, current: '0 MB', total: '0 MB', speed: '' };
|
||||
|
||||
|
||||
|
||||
window.electronAPI.onDownloadProgress((progress) => {
|
||||
(window as any).electronAPI.onDownloadProgress((progress: any) => {
|
||||
prog.value = {
|
||||
percentage: progress.percentage || 0,
|
||||
current: progress.current || '0 MB',
|
||||
total: progress.total || '0 MB',
|
||||
speed: progress.speed || ''
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await window.electronAPI.downloadUpdate(info.value.downloadUrl)
|
||||
const response = await (window as any).electronAPI.downloadUpdate(info.value.downloadUrl)
|
||||
|
||||
if (response.success) {
|
||||
stage.value = 'completed'
|
||||
@@ -181,10 +181,8 @@ async function start() {
|
||||
|
||||
async function cancelDownload() {
|
||||
try {
|
||||
if (window.electronAPI) {
|
||||
window.electronAPI.removeDownloadProgressListener()
|
||||
await window.electronAPI.cancelDownload()
|
||||
}
|
||||
(window as any).electronAPI.removeDownloadProgressListener()
|
||||
await (window as any).electronAPI.cancelDownload()
|
||||
show.value = false
|
||||
stage.value = 'check'
|
||||
} catch (error) {
|
||||
@@ -205,7 +203,7 @@ async function installUpdate() {
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
const response = await window.electronAPI.installUpdate()
|
||||
const response = await (window as any).electronAPI.installUpdate()
|
||||
|
||||
if (response.success) {
|
||||
ElMessage({ message: '应用即将重启', type: 'success' })
|
||||
@@ -231,9 +229,7 @@ onMounted(async () => {
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (window.electronAPI) {
|
||||
window.electronAPI.removeDownloadProgressListener()
|
||||
}
|
||||
(window as any).electronAPI.removeDownloadProgressListener()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ interface Emits {
|
||||
(e: 'reload'): void
|
||||
(e: 'user-click'): void
|
||||
(e: 'open-device'): void
|
||||
(e: 'open-settings'): void
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
@@ -45,7 +46,7 @@ defineEmits<Emits>()
|
||||
<button class="nav-btn-round" title="设备管理" @click="$emit('open-device')">
|
||||
<el-icon><Monitor /></el-icon>
|
||||
</button>
|
||||
<button class="nav-btn-round" title="设置">
|
||||
<button class="nav-btn-round" title="设置" @click="$emit('open-settings')">
|
||||
<el-icon><Setting /></el-icon>
|
||||
</button>
|
||||
<button class="nav-btn-round" title="用户" @click="$emit('user-click')">
|
||||
|
||||
@@ -3,6 +3,7 @@ import {ref, computed, onMounted} from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {rakutenApi} from '../../api/rakuten'
|
||||
import { batchConvertImages } from '../../utils/imageProxy'
|
||||
import { handlePlatformFileExport } from '../../utils/settings'
|
||||
|
||||
// UI 与加载状态
|
||||
const loading = ref(false)
|
||||
@@ -200,10 +201,15 @@ async function handleStartSearch() {
|
||||
progressPercentage.value = 0
|
||||
totalProducts.value = 0
|
||||
processedProducts.value = 0
|
||||
const resp = await rakutenApi.getProducts({file: pendingFile.value, batchId: currentBatchId.value})
|
||||
const products = (resp.products || []).map(p => ({...p, skuPrices: parseSkuPrices(p)}))
|
||||
allProducts.value = products
|
||||
pendingFile.value = null
|
||||
const resp = await rakutenApi.getProducts({file: pendingFile.value, batchId: currentBatchId.value})
|
||||
const products = (resp.products || []).map(p => ({...p, skuPrices: parseSkuPrices(p)}))
|
||||
|
||||
if (products.length === 0) {
|
||||
showMessage('未采集到数据,请检查代理或店铺是否存在', 'warning')
|
||||
}
|
||||
|
||||
allProducts.value = products
|
||||
pendingFile.value = null
|
||||
} catch (e) {
|
||||
statusType.value = 'error'
|
||||
statusMessage.value = '解析失败,请重试'
|
||||
@@ -367,12 +373,9 @@ async function exportToExcel() {
|
||||
const blob = new Blob([buffer], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
})
|
||||
const fileName = `乐天商品数据_${new Date().toISOString().slice(0, 10)}.xlsx`
|
||||
|
||||
const link = document.createElement('a')
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = `乐天商品数据_${new Date().toISOString().slice(0, 10)}.xlsx`
|
||||
link.click()
|
||||
URL.revokeObjectURL(link.href)
|
||||
await handlePlatformFileExport('rakuten', blob, fileName)
|
||||
|
||||
showMessage('Excel文件导出成功!', 'success')
|
||||
} catch (error) {
|
||||
@@ -412,8 +415,7 @@ onMounted(loadLatest)
|
||||
<div class="dropzone" @dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop="onDrop" @click="openRakutenUpload" :class="{ disabled: loading }">
|
||||
<div class="dz-el-icon">📤</div>
|
||||
<div class="dz-text">点击或将文件拖拽到这里上传</div>
|
||||
<div class="dz-sub">支持扩展名:.xls .xlsx</div>
|
||||
<div class="dz-sub">文件单列:1/1</div>
|
||||
<div class="dz-sub">支持 .xls .xlsx</div>
|
||||
</div>
|
||||
<input ref="uploadInputRef" style="display:none" type="file" accept=".xls,.xlsx" @change="handleExcelUpload" :disabled="loading"/>
|
||||
<div v-if="selectedFileName" class="file-chip">
|
||||
@@ -621,10 +623,10 @@ onMounted(loadLatest)
|
||||
.content-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; }
|
||||
|
||||
.left-controls { margin-top: 10px; display: flex; flex-direction: column; gap: 10px; }
|
||||
.dropzone { border: 1px dashed #c0c4cc; border-radius: 6px; padding: 16px; text-align: center; cursor: pointer; background: #fafafa; }
|
||||
.dropzone { border: 1px dashed #c0c4cc; border-radius: 6px; padding: 12px; text-align: center; cursor: pointer; background: #fafafa; }
|
||||
.dropzone:hover { background: #f6fbff; border-color: #409EFF; }
|
||||
.dropzone.disabled { opacity: .6; cursor: not-allowed; }
|
||||
.dz-el-icon { font-size: 20px; margin-bottom: 6px; color: #909399; }
|
||||
.dz-el-icon { font-size: 18px; margin-bottom: 4px; color: #909399; }
|
||||
.dz-text { color: #303133; font-size: 13px; }
|
||||
.dz-sub { color: #909399; font-size: 12px; }
|
||||
.single-input.left { display: flex; gap: 8px; }
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { zebraApi, type ZebraOrder, type BanmaAccount } from '../../api/zebra'
|
||||
import AccountManager from '../common/AccountManager.vue'
|
||||
import { batchConvertImages } from '../../utils/imageProxy'
|
||||
import { handlePlatformFileExport } from '../../utils/settings'
|
||||
|
||||
type Shop = { id: string; shopName: string }
|
||||
|
||||
@@ -234,12 +235,9 @@ async function exportToExcel() {
|
||||
const blob = new Blob([buffer], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
})
|
||||
const fileName = `斑马订单数据_${new Date().toISOString().slice(0, 10)}.xlsx`
|
||||
|
||||
const link = document.createElement('a')
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = `斑马订单数据_${new Date().toISOString().slice(0, 10)}.xlsx`
|
||||
link.click()
|
||||
URL.revokeObjectURL(link.href)
|
||||
await handlePlatformFileExport('zebra', blob, fileName)
|
||||
|
||||
showMessage('Excel文件导出成功!', 'success')
|
||||
} catch (error) {
|
||||
@@ -266,6 +264,7 @@ const formUsername = ref('')
|
||||
const formPassword = ref('')
|
||||
const rememberPwd = ref(true)
|
||||
const managerVisible = ref(false)
|
||||
const accountManagerRef = ref()
|
||||
|
||||
function openAddAccount() {
|
||||
isEditMode.value = false
|
||||
@@ -302,6 +301,9 @@ async function submitAccount() {
|
||||
accountDialogVisible.value = false
|
||||
await loadAccounts()
|
||||
if (id) accountId.value = id
|
||||
if (managerVisible.value && accountManagerRef.value?.load) {
|
||||
accountManagerRef.value.load()
|
||||
}
|
||||
} catch (e: any) {
|
||||
ElMessage({ message: e?.message || '账号或密码错误,无法获取Token', type: 'error' })
|
||||
}
|
||||
@@ -519,7 +521,7 @@ async function removeCurrentAccount() {
|
||||
<el-button type="primary" class="btn-blue" style="width: 100%" @click="submitAccount">登录</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<AccountManager v-model="managerVisible" platform="zebra" @add="openAddAccount" />
|
||||
<AccountManager ref="accountManagerRef" v-model="managerVisible" platform="zebra" @add="openAddAccount" @refresh="loadAccounts" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user