diff --git a/.deployrc.js b/.deployrc.js new file mode 100644 index 0000000..43da59e --- /dev/null +++ b/.deployrc.js @@ -0,0 +1,23 @@ +// .deployrc.js (ESM 格式) +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +export default { + host: '112.74.106.216', + port: 22, // 默认 SSH 端口 + username: 'root', + + // 密码和私钥二选一 + password: 'Smt1618?', + privateKey: null, // 示例: readFileSync(resolve(process.env.HOME, '.ssh/id_rsa')), + + // 部署路径 + remotePath: '/opt/1panel/apps/openresty/openresty/www/sites/www.amzups.com/index', + + // 是否清空目标目录 + cleanRemote: true + + // 高级选项 (可选) + // uploadIgnore: ['*.map', 'tmp/*'], + // execAfterDeploy: 'sudo systemctl restart nginx' +}; diff --git a/.env.development b/.env.development index 1ac6eef..35cdc83 100644 --- a/.env.development +++ b/.env.development @@ -7,6 +7,10 @@ VITE_APP_ENV = 'development' # 开发环境 VITE_APP_BASE_API = '/dev-api' +VITE_BACKEND_URL = 'http://localhost:8080' + +#VITE_BACKEND_URL = 'http://112.74.106.216:8080' + # 应用访问路径 例如使用前缀 /admin/ VITE_APP_CONTEXT_PATH = '/' diff --git a/.env.production b/.env.production index 2fa42b0..6f68aab 100644 --- a/.env.production +++ b/.env.production @@ -16,6 +16,8 @@ VITE_APP_SNAILJOB_ADMIN = '/snail-job' # 生产环境 VITE_APP_BASE_API = '/prod-api' +VITE_BACKEND_URL = 'http://112.74.106.216:8080' + # 是否在打包时开启压缩,支持 gzip 和 brotli VITE_BUILD_COMPRESS = gzip diff --git a/deploy.js b/deploy.js new file mode 100644 index 0000000..5bb98cd --- /dev/null +++ b/deploy.js @@ -0,0 +1,90 @@ +import { NodeSSH } from 'node-ssh'; +import path from 'node:path'; +import fs from 'node:fs'; +import process from 'node:process'; + +// 动态导入配置文件(支持文件不存在的情况) +const loadConfig = async () => { + try { + const { default: config } = await import('./.deployrc.js'); + return config; + } catch (error) { + if (error.code !== 'ERR_MODULE_NOT_FOUND') throw error; + + return { + host: process.env.DEPLOY_HOST, + port: process.env.DEPLOY_PORT || 22, + username: process.env.DEPLOY_USER, + password: process.env.DEPLOY_PASSWORD, + privateKey: process.env.DEPLOY_PRIVATE_KEY_PATH ? fs.readFileSync(path.resolve(process.env.DEPLOY_PRIVATE_KEY_PATH)) : null, + remotePath: process.env.DEPLOY_REMOTE_PATH, + cleanRemote: process.env.DEPLOY_CLEAN === 'true' + }; + } +}; + +const ssh = new NodeSSH(); + +async function deploy() { + try { + // 加载配置 + const config = await loadConfig(); + validateConfig(config); + + // 连接服务器 + console.log('🔄 Connecting to server...'); + await ssh.connect({ + host: config.host, + port: config.port, + username: config.username, + password: config.password, + privateKey: config.privateKey + }); + + // 确保目录存在 + console.log('📂 Ensuring remote directory exists...'); + await ssh.execCommand(`mkdir -p ${config.remotePath}`); + + // 清理目录 + if (config.cleanRemote) { + console.log('🧹 Cleaning remote directory...'); + await ssh.execCommand(`rm -rf ${config.remotePath}/*`); + } + + // 上传文件 + console.log('🚀 Uploading files...'); + const uploadResult = await ssh.putDirectory('./dist', config.remotePath, { + recursive: true, + concurrency: 10, + tick: (localPath, remotePath, error) => { + const relativePath = path.relative(process.cwd(), localPath); + console[error ? 'error' : 'log'](`${error ? '❌' : '✅'} ${error ? 'Failed' : 'Uploaded'}: ${relativePath}`); + } + }); + + if (!uploadResult) throw new Error('Upload failed without specific error'); + + console.log('🎉 Deployment completed successfully!'); + } catch (error) { + console.error('🔥 Deployment failed:', error.message); + process.exit(1); + } finally { + ssh.dispose(); + } +} + +function validateConfig(config) { + const requiredFields = ['host', 'username', 'remotePath']; + const missing = requiredFields.filter((field) => !config[field]); + + if (missing.length) { + throw new Error(`Missing required config fields: ${missing.join(', ')}`); + } + + if (!config.password && !config.privateKey) { + throw new Error('Either password or privateKey must be provided'); + } +} + +// 执行部署 +deploy(); diff --git a/eslint.config.js b/eslint.config.js index 8b67e9e..7a17c69 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -60,6 +60,8 @@ export default [ globals: { // 自动导入的配置 undef ...autoImportGlobals.globals, + process: 'readonly', + require: 'readonly', DialogOption: 'readonly', LayoutSetting: 'readonly' } diff --git a/package.json b/package.json index c13ab54..fa89b2e 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "scripts": { "dev": "vite serve --mode development", "build:prod": "vite build --mode production", + "deploy": "node deploy.js", + "build-and-deploy": "npm run build:prod && npm run deploy", "build:dev": "vite build --mode development", "preview": "vite preview", "lint:eslint": "eslint", @@ -40,6 +42,7 @@ "image-conversion": "2.1.1", "js-cookie": "3.0.5", "jsencrypt": "3.3.2", + "node-ssh": "^13.2.1", "nprogress": "0.2.0", "pinia": "2.2.6", "screenfull": "6.0.2", diff --git a/public/favicon.ico b/public/favicon.ico index 3f919d8..a23e661 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/src/api/amz/inquiryRequest/index.ts b/src/api/amz/inquiryRequest/index.ts index b7409a5..7534d79 100644 --- a/src/api/amz/inquiryRequest/index.ts +++ b/src/api/amz/inquiryRequest/index.ts @@ -29,7 +29,7 @@ export const getInquiryRequest = (id: string | number): AxiosPromise => { +export const createWithDesAndChannel = (destination: string | number, channelId: string, date: string): AxiosPromise => { return request({ url: '/amz/inquiryRequest/create/' + destination + '/' + channelId + '/' + date, method: 'get' @@ -44,7 +44,7 @@ export const createWithDesAndChannel = (destination: string, channelId: string, * @param date */ -export const queryWithDesAndChannel = (destination: string, channelId: string, date: string): AxiosPromise => { +export const queryWithDesAndChannel = (destination: string | number, channelId: string, date: string): AxiosPromise => { return request({ url: '/amz/inquiryRequest/query/' + destination + '/' + channelId + '/' + date, method: 'get' diff --git a/src/api/amz/logisticsOrder/index.ts b/src/api/amz/logisticsOrder/index.ts index ad7a7a6..32fc904 100644 --- a/src/api/amz/logisticsOrder/index.ts +++ b/src/api/amz/logisticsOrder/index.ts @@ -1,6 +1,6 @@ import request from '@/utils/request'; import { AxiosPromise } from 'axios'; -import { LogisticsOrderVO, LogisticsOrderForm, LogisticsOrderQuery, createOrderForm } from '@/api/amz/logisticsOrder/types'; +import { LogisticsOrderVO, LogisticsOrderForm, LogisticsOrderQuery, createOrderForm, InquiryQuoteStatusVO } from '@/api/amz/logisticsOrder/types'; /** * 查询物流订单列表 @@ -20,13 +20,24 @@ export const listLogisticsOrder = (query?: LogisticsOrderQuery): AxiosPromise => { +export const getLogisticsOrder = (id: string | number): AxiosPromise => { return request({ url: '/amz/logisticsOrder/' + id, method: 'get' }); }; +// /** +// * 查询物流订单详细 +// * @param id +// */ +// export const getTodayQuoteStatus = (): AxiosPromise => { +// return request({ +// url: '/amz/logisticsOrder/today-quote-status', +// method: 'get' +// }); +// }; + /** * 新增物流订单 * @param data diff --git a/src/api/amz/logisticsOrder/types.ts b/src/api/amz/logisticsOrder/types.ts index 2dda125..0b5870c 100644 --- a/src/api/amz/logisticsOrder/types.ts +++ b/src/api/amz/logisticsOrder/types.ts @@ -55,6 +55,18 @@ export interface LogisticsOrderVO { shelfTimeliness: number; } +export interface InquiryQuoteStatusVO { + // private Long inquiryId; + // private String inquiryNo; + // private Long providerId; + // private String quoteStatus; // "已报价" 或 "未报价" + + inquiryId: string | number; + inquiryNo: string; + providerId: string | number; + quoteStatus: string; +} + export interface LogisticsOrderForm extends BaseEntity { /** * 主键(应用层生成的全局唯一ID,如雪花算法) @@ -84,7 +96,7 @@ export interface LogisticsOrderForm extends BaseEntity { /** * 物流渠道(如空运/海运/快递等) */ - logisticsChannel?: string; + channelName?: string; /** * 目的地仓库名称或编码 diff --git a/src/api/amz/logisticsQuote/index.ts b/src/api/amz/logisticsQuote/index.ts index 420ded8..741a69e 100644 --- a/src/api/amz/logisticsQuote/index.ts +++ b/src/api/amz/logisticsQuote/index.ts @@ -1,6 +1,8 @@ import request from '@/utils/request'; import { AxiosPromise } from 'axios'; import { LogisticsQuoteVO, LogisticsQuoteForm, LogisticsQuoteQuery } from '@/api/amz/logisticsQuote/types'; +import { InquiryQuoteStatusVO } from '@/api/amz/logisticsOrder/types'; +import { UnwrapRef } from 'vue'; /** * 查询物流报价列表 @@ -24,7 +26,7 @@ export const listLogisticsQuote = (query?: LogisticsQuoteQuery): AxiosPromise => { +export const queryLogisticsQuote = (destination: string | number, channelId: string, date: string): AxiosPromise => { return request({ url: '/amz/logisticsQuote/query/' + destination + '/' + channelId + '/' + date, method: 'get' @@ -46,7 +48,7 @@ export const getLogisticsQuote = (id: string | number): AxiosPromise { +export const getTodayQuoteStatus = (): AxiosPromise => { return request({ url: '/amz/logisticsQuote/today-quote-status', method: 'get' @@ -77,6 +79,18 @@ export const addMostLogisticsQuote = (data: LogisticsQuoteForm) => { }); }; +/** + * 获取物流报价建议 + * @param data + */ +export const checkPriceQuoteByBo = (data: LogisticsQuoteForm) => { + return request({ + url: '/amz/logisticsQuote/check-quote-price', + method: 'post', + data: data + }); +}; + /** * 修改物流报价 * @param data diff --git a/src/api/amz/shipmentPlan/index.ts b/src/api/amz/shipmentPlan/index.ts index bec5fca..e2ed043 100644 --- a/src/api/amz/shipmentPlan/index.ts +++ b/src/api/amz/shipmentPlan/index.ts @@ -16,6 +16,14 @@ export const listShipmentPlan = (query?: ShipmentPlanQuery): AxiosPromise => { + return request({ + url: '/amz/shipmentPlan/list/order', + method: 'get', + params: query + }); +}; + /** * 查询货件计划详细 * @param id @@ -27,6 +35,16 @@ export const getShipmentPlan = (id: string | number): AxiosPromise { + return request({ + url: '/amz/shipmentPlan/take-today-amz-plan-data', + method: 'get' + }); +}; /** * 查询货件计划详细 diff --git a/src/api/amz/shipmentPlan/types.ts b/src/api/amz/shipmentPlan/types.ts index cbb3ba7..db3d286 100644 --- a/src/api/amz/shipmentPlan/types.ts +++ b/src/api/amz/shipmentPlan/types.ts @@ -1,3 +1,7 @@ +import { LogisticsOrderVO } from '@/api/amz/logisticsOrder/types'; +import { LogisticsOrderDetailVO } from '@/api/amz/logisticsOrderDetail/types'; +import { ShipmentItemVO } from '@/api/amz/shipmentItem/types'; + export interface ShipmentPlanVO { /** * 主键ID @@ -32,7 +36,7 @@ export interface ShipmentPlanVO { /** * 物流中心编码 */ - destination: string | number; + destination: string; /** * 运输模式 @@ -57,7 +61,7 @@ export interface ShipmentPlanVO { /** * 同步时间 */ - syncTime: string; + receivingTime: string; /** * 计划发货日期 @@ -99,7 +103,205 @@ export interface ShipmentPlanVO { */ isSta: string; + /** + * 货件唯一编号 + */ + shipmentUniqueId: string | number; + /** + * STA任务编号 + */ + inboundPlanId: string | number; + + /** + * 运营操作货件状态 + */ + fbaStatus: string; + + /** + * 总箱子数量 + */ + boxQuantity: number; + + /** + * 箱子尺寸 + */ + boxSize: string; + + /** + * 供应商称重(单位:KG,物流商实际测量值) + */ + vendorWeight: number; + + /** + * 套数(系统中的申报量) + */ + setTotal: number; + + /** + * 渠道ID + */ + channelId: string | number; + + /** + * 物流渠道 + */ + channelName: string; +} + +export interface ShipmentPlanOrderVO { + /** + * 主键ID + */ + id: string | number; + + /** + * 关联系统ID + */ + sid: string | number; + + /** + * 货件编号 + */ + shipmentId: string | number; + + /** + * 货件名称 + */ + shipmentName: string; + + /** + * 是否关闭 + */ + isClosed: string; + + /** + * 货件状态 + */ + shipmentStatus: string; + + /** + * 物流中心编码 + */ + destination: string; + + /** + * 运输模式 + */ + shippingMode: string; + + /** + * 运输方案 + */ + shippingSolution: string; + + /** + * 最后更新时间 + */ + gmtModified: string; + + /** + * 创建时间 + */ + gmtCreate: string; + + /** + * 同步时间 + */ + receivingTime: string; + + /** + * 计划发货日期 + */ + staShipmentDate: string; + + /** + * 预计到货开始日 + */ + staDeliveryStartDate: string; + + /** + * 预计到货截止日 + */ + staDeliveryEndDate: string; + + /** + * 发货地址 + */ + shipFromAddress: string; + + /** + * 收货地址 + */ + shipToAddress: string; + + /** + * 参考编号 + */ + referenceId: string | number; + + /** + * 入库计划ID + */ + staInboundPlanId: string | number; + + /** + * 是否STA计划 + */ + isSta: string; + + /** + * 货件唯一编号 + */ + shipmentUniqueId: string | number; + + /** + * STA任务编号 + */ + inboundPlanId: string | number; + + /** + * 运营操作货件状态 + */ + fbaStatus: string; + + /** + * 总箱子数量 + */ + boxQuantity: number; + + /** + * 箱子尺寸 + */ + boxSize: string; + + /** + * 供应商称重(单位:KG,物流商实际测量值) + */ + logisticsWeight: number; + + /** + * 套数(系统中的申报量) + */ + setTotal: number; + + /** + * 渠道ID + */ + channelId: string | number; + + /** + * 物流渠道 + */ + channelName: string; + + order?: LogisticsOrderVO; + + detailList?: LogisticsOrderDetailVO[]; + + itemVoList?: ShipmentItemVO[]; + + quote?: LogisticsOrderVO; } export interface ShipmentPlanForm extends BaseEntity { @@ -136,7 +338,7 @@ export interface ShipmentPlanForm extends BaseEntity { /** * 物流中心编码 */ - destination?: string | number; + destination?: string; /** * 运输模式 @@ -161,7 +363,7 @@ export interface ShipmentPlanForm extends BaseEntity { /** * 同步时间 */ - syncTime?: string; + receivingTime?: string; /** * 计划发货日期 @@ -203,10 +405,53 @@ export interface ShipmentPlanForm extends BaseEntity { */ isSta?: string; + /** + * 货件唯一编号 + */ + shipmentUniqueId?: string | number; + + /** + * STA任务编号 + */ + inboundPlanId?: string | number; + + /** + * 运营操作货件状态 + */ + fbaStatus?: string; + + /** + * 总箱子数量 + */ + boxQuantity?: number; + + /** + * 箱子尺寸 + */ + boxSize?: string; + + /** + * 供应商称重 + */ + vendorWeight?: number; + + /** + * 套数(系统中的申报量) + */ + setTotal?: number; + + /** + * 渠道ID + */ + channelId?: string | number; + + /** + * 物流渠道 + */ + channelName?: string; } export interface ShipmentPlanQuery extends PageQuery { - /** * 关联系统ID */ @@ -235,7 +480,7 @@ export interface ShipmentPlanQuery extends PageQuery { /** * 物流中心编码 */ - destination?: string | number; + destination?: string; /** * 运输模式 @@ -260,7 +505,7 @@ export interface ShipmentPlanQuery extends PageQuery { /** * 同步时间 */ - syncTime?: string; + receivingTime?: string; /** * 计划发货日期 @@ -302,11 +547,53 @@ export interface ShipmentPlanQuery extends PageQuery { */ isSta?: string; - /** - * 日期范围参数 - */ - params?: any; + /** + * 货件唯一编号 + */ + shipmentUniqueId?: string | number; + + /** + * STA任务编号 + */ + inboundPlanId?: string | number; + + /** + * 运营操作货件状态 + */ + fbaStatus?: string; + + /** + * 总箱子数量 + */ + boxQuantity?: number; + + /** + * 箱子尺寸 + */ + boxSize?: string; + + /** + * 供应商称重(单位:KG,物流商实际测量值) + */ + logisticsWeight?: number; + + /** + * 套数(系统中的申报量) + */ + setTotal?: number; + + /** + * 渠道ID + */ + channelId?: string | number; + + /** + * 物流渠道 + */ + channelName?: string; + + /** + * 日期范围参数 + */ + params?: any; } - - - diff --git a/src/api/amz/shipmentTracking/types.ts b/src/api/amz/shipmentTracking/types.ts index 6f7132e..525dc68 100644 --- a/src/api/amz/shipmentTracking/types.ts +++ b/src/api/amz/shipmentTracking/types.ts @@ -19,6 +19,66 @@ export interface ShipmentTrackingVO { */ trackingNumber: string; + /** + * 本地箱子编号 + */ + localBoxId: string | number; + + /** + * 包裹唯一标识 + */ + packageId: string | number; + + /** + * 总数量 + */ + total: number; + + /** + * 重量 + */ + weight: number; + + /** + * 重量单位 + */ + weightUnit: string; + + /** + * 长度 + */ + length: number; + + /** + * 宽度 + */ + width: string | number; + + /** + * 高度 + */ + height: number; + + /** + * 尺寸单位 + */ + lengthUnit: string; + + /** + * 箱子展示名称 + */ + boxName: string; + + /** + * 货件唯一编号 + */ + shipmentUniqueId: string | number; + + /** + * STA任务编号 + */ + inboundPlanId: string | number; + } export interface ShipmentTrackingForm extends BaseEntity { @@ -42,6 +102,66 @@ export interface ShipmentTrackingForm extends BaseEntity { */ trackingNumber?: string; + /** + * 本地箱子编号 + */ + localBoxId?: string | number; + + /** + * 包裹唯一标识 + */ + packageId?: string | number; + + /** + * 总数量 + */ + total?: number; + + /** + * 重量 + */ + weight?: number; + + /** + * 重量单位 + */ + weightUnit?: string; + + /** + * 长度 + */ + length?: number; + + /** + * 宽度 + */ + width?: string | number; + + /** + * 高度 + */ + height?: number; + + /** + * 尺寸单位 + */ + lengthUnit?: string; + + /** + * 箱子展示名称 + */ + boxName?: string; + + /** + * 货件唯一编号 + */ + shipmentUniqueId?: string | number; + + /** + * STA任务编号 + */ + inboundPlanId?: string | number; + } export interface ShipmentTrackingQuery extends PageQuery { @@ -61,6 +181,66 @@ export interface ShipmentTrackingQuery extends PageQuery { */ trackingNumber?: string; + /** + * 本地箱子编号 + */ + localBoxId?: string | number; + + /** + * 包裹唯一标识 + */ + packageId?: string | number; + + /** + * 总数量 + */ + total?: number; + + /** + * 重量 + */ + weight?: number; + + /** + * 重量单位 + */ + weightUnit?: string; + + /** + * 长度 + */ + length?: number; + + /** + * 宽度 + */ + width?: string | number; + + /** + * 高度 + */ + height?: number; + + /** + * 尺寸单位 + */ + lengthUnit?: string; + + /** + * 箱子展示名称 + */ + boxName?: string; + + /** + * 货件唯一编号 + */ + shipmentUniqueId?: string | number; + + /** + * STA任务编号 + */ + inboundPlanId?: string | number; + /** * 日期范围参数 */ diff --git a/src/assets/images/profile.jpg b/src/assets/images/profile.jpg index f4fde57..507537a 100644 Binary files a/src/assets/images/profile.jpg and b/src/assets/images/profile.jpg differ diff --git a/src/assets/logo/logo.png b/src/assets/logo/logo.png index 3f919d8..a23e661 100644 Binary files a/src/assets/logo/logo.png and b/src/assets/logo/logo.png differ diff --git a/src/components/EnhancedTable/index.vue b/src/components/EnhancedTable/index.vue new file mode 100644 index 0000000..1ada770 --- /dev/null +++ b/src/components/EnhancedTable/index.vue @@ -0,0 +1,98 @@ + + + + + + diff --git a/src/components/LTable/index.vue b/src/components/LTable/index.vue new file mode 100644 index 0000000..c5d399e --- /dev/null +++ b/src/components/LTable/index.vue @@ -0,0 +1,126 @@ + + diff --git a/src/components/OrderDetailFileUpload/index.vue b/src/components/OrderDetailFileUpload/index.vue new file mode 100644 index 0000000..6d6b49b --- /dev/null +++ b/src/components/OrderDetailFileUpload/index.vue @@ -0,0 +1,237 @@ + + + + + diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue index 4a19ad3..a6b9494 100644 --- a/src/layout/components/Navbar.vue +++ b/src/layout/components/Navbar.vue @@ -129,6 +129,8 @@ const roleNmae = computed(() => { return '物流商'; } else if (roles.includes('yunying')) { return '运营'; + } else if (roles.includes('manager')) { + return '管理员'; } else { return '普通用户'; } diff --git a/src/views/amz/amazonStore/index.vue b/src/views/amz/amazonStore/index.vue index f7bf4fe..93be6f9 100644 --- a/src/views/amz/amazonStore/index.vue +++ b/src/views/amz/amazonStore/index.vue @@ -91,6 +91,9 @@ + + + @@ -152,6 +155,7 @@ import { listAmazonStore, getAmazonStore, delAmazonStore, addAmazonStore, updateAmazonStore, collectAmzStoreData } from '@/api/amz/amazonStore'; import { AmazonStoreVO, AmazonStoreQuery, AmazonStoreForm } from '@/api/amz/amazonStore/types'; import AssignUser from '@/views/amz/amazonStore/assignUser.vue'; +import { getInquiryRequest } from '@/api/amz/inquiryRequest'; const { proxy } = getCurrentInstance() as ComponentInternalInstance; @@ -283,6 +287,17 @@ const handleAdd = () => { /** 修改按钮操作 */ const handleUpdate = async (row?: AmazonStoreVO) => { + reset(); + const _id = row?.id || ids.value[0]; + form.value.id = _id; + console.log('form.value.id', _id); + const res = await getInquiryRequest(_id); + Object.assign(form.value, res.data); + dialog.visible = true; + dialog.title = '修改亚马逊店铺信息'; +}; + +const changeUser = async (row?: AmazonStoreVO) => { reset(); const _id = row?.id || ids.value[0]; form.value.id = _id; diff --git a/src/views/amz/inquiryRequest/index.vue b/src/views/amz/inquiryRequest/index.vue index 57c7211..8b1054f 100644 --- a/src/views/amz/inquiryRequest/index.vue +++ b/src/views/amz/inquiryRequest/index.vue @@ -234,6 +234,12 @@
+ + + + 到