Commit 9c8ec360 by wuqian

Merge branch 'dev' of 47.122.114.111:qinjianhui/factory_front into new-pod-cn

parents 6d3b965d 7a0fd357
......@@ -12,7 +12,8 @@
"rules": {
"vue/require-default-prop": "off",
"vue/multi-word-component-names": "off",
"no-console": "off","no-unused-vars": "off",
"no-console": "off",
"@typescript-eslint/no-explicit-any": "error"
},
"overrides": [{ "files": "*.vue", "rules": { "no-undef": "off" } }]
}
......@@ -32,7 +32,6 @@ declare module 'vue' {
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElLink: typeof import('element-plus/es')['ElLink']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
......@@ -65,6 +64,7 @@ declare module 'vue' {
RouterView: typeof import('vue-router')['RouterView']
Select: typeof import('./src/components/Form/Select.vue')['default']
ShipmentOrderDetail: typeof import('./src/components/ShipmentOrderDetail.vue')['default']
SideBar: typeof import('./src/components/SideBar.vue')['default']
SplitDiv: typeof import('./src/components/splitDiv/splitDiv.vue')['default']
'Switch ': typeof import('./src/components/Form/Switch .vue')['default']
TableView: typeof import('./src/components/TableView.vue')['default']
......
import { BaseRespData } from '@/types/api'
import axios from './axios'
import { LogisticsData } from '@/types/api/order'
import { LogisticBill } from '@/types/api/podMakeOrder'
import { userData } from '@/types/api/user'
import { VersionImageList } from '@/types/api/typesetting'
......@@ -36,14 +38,14 @@ export function uploadFileApi(data: FormData) {
// 打印物流面单 US
export function getLogisticUSApi(content: string) {
return axios.get<never, BaseRespData<{ documentUrl: string }>>(
`factory/podJomallOrderUs/getDocument?content=${content}`,
return axios.get<never, BaseRespData<LogisticBill>>(
`factory/podJomallOrderUs/getOrderByFactorySubOrderNumber?factorySubOrderNumber=${content}`,
)
}
// 打印物流面单 CN
export function getLogisticCNApi(content: string) {
return axios.get<never, BaseRespData<{ documentUrl: string }>>(
`factory/podJomallOrder/getDocument?content=${content}`,
return axios.get<never, BaseRespData<LogisticBill>>(
`factory/podJomallOrder/getOrderByThirdSubOrderNumber?thirdSubOrderNumber=${content}`,
)
}
......@@ -2,11 +2,11 @@ import { BasePaginationData, BaseRespData } from '@/types/api'
import axios from './axios'
import { ExternalAuthListData } from '@/types/api/externalAuth'
export function getExternalAuthorisationListApi(
data: {
type: string
data?: {
type?: string
},
page: number,
pageSize: number,
page?: number,
pageSize?: number,
) {
return axios.post<never, BasePaginationData<ExternalAuthListData>>(
'factory/baseExternalAccount/list_page',
......@@ -36,6 +36,12 @@ export function deleteExternalAuthorisationApi(ids: string) {
`factory/baseExternalAccount/delete?ids=${ids}`,
)
}
export function baseExternalAccountLogsApi(params: { ids: string }) {
return axios.get<never, BaseRespData<never>>(
`factory/baseExternalAccount/logs`,
{ params },
)
}
export function addExternalAuthorisationApi(
url: string,
data: ExternalAuthListData,
......
......@@ -144,6 +144,12 @@ export function getUniuniList() {
},
)
}
// 获取tictok物流承运商
export function getTiktokCarrier() {
return axios.get<never, BaseRespData<{ name: string; id: number }[]>>(
'logisticsWay/getTiktokShippingProvider',
)
}
/**
* @description 发货地址
......
......@@ -425,7 +425,7 @@ export function arrangeFinishApi(ids: number[]) {
// 获取工艺
export function getListCraftApi() {
return axios.post<never, BaseRespData<never>>(
`factory/podJomallOrderProductUs/listCraft`,
`factory/podJomallOrderProductCn/listCraft`,
)
}
......
......@@ -544,3 +544,35 @@ export function completeDeliveryApi(params: {
params,
)
}
// 物流轨迹
export function get17TrackInfoApi(params: { id: string | number }) {
return axios.get(`factory/podJomallOrderUs/get17TrackInfo`, { params })
}
// 获取riin 权限
export function getAccountCodeByFactoryIdApi(params: { token: string }) {
return axios.get(`factory/baseExternalAccount/getAccountCodeByFactoryId`, {
params,
})
}
// 打印拣货单item
export function printPickPdfByBatchNumberApi(params: {
batchArrangeNumber: string
}) {
return axios.get<never, BaseRespData<never>>(
`factory/podJomallOrderProductUs/printPickPdfByBatchNumber`,
{
params,
},
)
}
// 打印生产单item
export function printProductionPdfByBatchNumberApi(params: {
batchArrangeNumber: string
}) {
return axios.get<never, BaseRespData<never>>(
`factory/podJomallOrderProductUs/printProductionPdfByBatchNumber`,
{
params,
},
)
}
......@@ -76,36 +76,40 @@ import type { TypesettingListData } from '../types/api/typesetting'
// 定义通用字段接口,用于处理动态属性
interface CommonFields {
[key: string]: unknown;
variantImage?: string;
mainImage?: string;
sku?: string;
productName?: string;
[key: string]: unknown
variantImage?: string
mainImage?: string
sku?: string
productName?: string
}
// 定义图片列表项接口
interface ImageListItem {
[key: string]: unknown;
imagePath?: string;
[key: string]: unknown
imagePath?: string
}
// 创建一个工具类型,用于使用字符串索引访问对象属性
type IndexableObject = Record<string, unknown>;
type IndexableObject = Record<string, unknown>
// 扩展现有类型以确保它们有通用字段
type CardItem = PodProductList | CardOrderData | TypesettingListData | CommonFields;
type CardItem =
| PodProductList
| CardOrderData
| TypesettingListData
| CommonFields
// 定义 props 类型
interface Props {
cardItem: CardItem;
active?: boolean;
showSelectIcon?: boolean;
showSku?: boolean;
showProductInfo?: boolean;
showImageList?: boolean;
imageField?: string;
imageListField?: string;
imagePathField?: string;
cardItem: CardItem
active?: boolean
showSelectIcon?: boolean
showSku?: boolean
showProductInfo?: boolean
showImageList?: boolean
imageField?: string
imageListField?: string
imagePathField?: string
}
// 定义默认值
......@@ -122,45 +126,45 @@ const props = withDefaults(defineProps<Props>(), {
// 获取主图片源
const mainImageSrc = computed<string>(() => {
const item = props.cardItem as IndexableObject;
const item = props.cardItem as IndexableObject
// 使用索引访问避免联合类型的属性访问问题
if (
props.imageField === 'variantImage' &&
typeof item[props.imageField] === 'string'
) {
return item[props.imageField] as string;
return item[props.imageField] as string
}
if (
props.imageField === 'mainImage' &&
typeof item[props.imageField] === 'string'
) {
return item[props.imageField] as string;
return item[props.imageField] as string
}
// 默认返回空字符串
return '';
return ''
})
// 获取图片列表
const imageList = computed<ImageListItem[]>(() => {
const item = props.cardItem as IndexableObject;
const list = item[props.imageListField];
return Array.isArray(list) ? list as ImageListItem[] : [];
const item = props.cardItem as IndexableObject
const list = item[props.imageListField]
return Array.isArray(list) ? (list as ImageListItem[]) : []
})
// 判断是否有图片列表
const hasImageList = computed<boolean>(() => {
return imageList.value.length > 0;
return imageList.value.length > 0
})
// 获取图片列表项的图片路径
function getItemImagePath(item: IndexableObject): string {
return (item[props.imagePathField] as string) || '';
return (item[props.imagePathField] as string) || ''
}
const copy = (text: string) => {
navigator.clipboard.writeText(text);
ElMessage.success('复制成功');
navigator.clipboard.writeText(text)
ElMessage.success('复制成功')
}
</script>
......@@ -174,11 +178,16 @@ const copy = (text: string) => {
.commodity-card-image {
position: relative;
border: 1px solid #eee;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
.img_top_left {
position: absolute;
top: 5px;
left: 5px;
display: flex;
align-items: center;
gap: 4px;
}
.img_top_right {
......
......@@ -34,6 +34,7 @@
</el-sub-menu>
</template>
</el-menu>
<SideBar />
<div v-if="userInfo" class="user-info">
<span class="user-avatar" style="color: #fff; font-size: 14px">
工厂: {{ userInfo.factoryCode }}
......@@ -111,19 +112,6 @@
<ArrowRight />
</el-icon>
</div>
<!-- 右侧工具栏 -->
<div class="tool_warper">
<div title="格式工具" class="tool-item" @click="formatDrawer = true">
<img src="../assets/images/brush-no.png" width="24" height="24" />
</div>
<div
title="打印物流面单"
class="tool-item"
@click="getLogisticDrawer = true"
>
<img src="../assets/images/printer.png" width="24" height="24" />
</div>
</div>
</div>
<el-dialog
v-model="dialogVisible"
......@@ -170,69 +158,6 @@
</span>
</template>
</el-dialog>
<span>
<el-drawer
v-model="formatDrawer"
class="format-drawer"
title="格式工具"
direction="rtl"
size="45%"
>
<el-input
v-model="textareaT"
style="width: 100%"
:rows="10"
type="textarea"
placeholder="请输入"
@input="confimTools"
/>
<div
style="
margin-top: 10px;
height: 250px;
overflow: auto;
border: 1px solid #ddd;
padding: 10px;
"
>
{{ newTextareaT }}
</div>
<template #footer>
<span style="display: flex; justify-content: center">
<el-button @click="formatDrawer = false"> 取 消 </el-button>
<el-button type="primary" @click="copy"> 拷 贝 </el-button>
</span>
</template>
</el-drawer>
<el-drawer
v-model="getLogisticDrawer"
direction="btt"
class="get-logistic-drawer"
title="打印物流面单"
@close="content = ''"
>
<ElSelect
v-model="sheetPrinter"
placeholder="请选择打印机"
@change="handlePrinterChange"
style="width: 200px; margin-right: 10px"
>
<ElOption
v-for="item in printDeviceList"
:key="item"
:label="item"
:value="item"
/>
</ElSelect>
<el-input
v-model="content"
placeholder="请输入单号"
style="width: 300px; margin-right: 10px"
clearable
/>
<el-button type="primary" @click="getLogistic">查 询</el-button>
</el-drawer>
</span>
</template>
<script setup lang="ts">
import {
......@@ -257,12 +182,12 @@ import userUserStore from '@/store/user'
import type { FormRules } from 'element-plus'
import { useValue } from '@/utils/hooks/useValue'
import useLodop from '@/utils/hooks/useLodop'
import { changePasswordApi } from '@/api/auth'
import { getLogisticUSApi, getLogisticCNApi } from '@/api/common'
import { filePath, FileCnPath } from '@/api/axios'
import { ElMessage } from 'element-plus'
import SideBar from './SideBar.vue'
interface MenuItem {
index: string
id: number
......@@ -287,14 +212,6 @@ const menuList = reactive(Menu)
const userStore = userUserStore()
const userInfo = userStore.user
const dialogVisible = ref(false)
const formatDrawer = ref(false)
const getLogisticDrawer = ref(false)
const printDeviceList = ref<string[]>([])
const sheetPrinter = ref('')
const textareaT = ref('')
const newTextareaT = ref('')
const content = ref('')
// 密码form
const [passwordForm, resetPasswordForm] = useValue<PasswordForm>(
{} as PasswordForm,
......@@ -486,102 +403,6 @@ const ensureActiveTabVisible = () => {
})
}
// 格式工具输入
const confimTools = (v: string) => {
// 先处理结尾的多个空格,只保留一个
let text = v.replace(/\s+$/, ' ')
// 处理开头的多个空格,只保留一个
text = text.replace(/^\s+/, ' ')
// 将多个连续空格替换为单个空格
text = text.replace(/\s+/g, ' ')
// 然后将所有空格和换行符替换为逗号
text = text.replace(/(\s|[\r\n])/g, ',')
// 如果结尾是逗号,则删除
if (text.slice(-1) === ',') {
newTextareaT.value = text.slice(0, -1)
} else {
newTextareaT.value = text
}
}
// 复制
const copy = () => {
const oInput = document.createElement('input')
oInput.value = newTextareaT.value
document.body.appendChild(oInput)
oInput.select() // 选择对象
document.execCommand('Copy') // 执行浏览器复制命令
oInput.className = 'oInput'
oInput.style.display = 'none'
document.body.removeChild(oInput)
ElMessage({
message: '复制成功',
type: 'success',
})
}
const { getCLodop } = useLodop()
const initPrintDevice = () => {
const lodop = getCLodop(null, null)
if (!lodop) return
const arr = []
// 获取打印机数量
const length = lodop.GET_PRINTER_COUNT()
for (let i = 0; i < length; i++) {
// 根据设备序号获取设备名
const name = lodop.GET_PRINTER_NAME(i)
arr.push(name)
}
// 获取默认打印机
sheetPrinter.value = lodop.GET_PRINTER_NAME(0)
printDeviceList.value = arr
}
// 处理打印机选择变化,保存到 localStorage
const handlePrinterChange = (value: string) => {
sheetPrinter.value = value
localStorage.setItem('sheetPrinter', JSON.stringify(value))
}
// 打印物流面单
const getLogistic = () => {
if (!content.value) {
ElMessage.warning('请输入单号')
return
}
// 按_分割字符串
const parts = content.value.split('_')
// 检查是否有足够的_分隔符
if (parts.length < 4) {
ElMessage.error('单号格式错误,请检查输入')
return
}
// 获取第三个_后面的内容
const thirdPart = parts[3]
if (thirdPart.startsWith('USPSC')) {
// 美国物流
getLogisticUSApi(content.value).then((res) => {
if (res.code === 200) {
window.open(filePath + res.data.documentUrl, '_blank')
}
})
} else if (thirdPart.startsWith('JMPSC')) {
// 中国物流
getLogisticCNApi(content.value).then((res) => {
if (res.code === 200) {
window.open(FileCnPath + res.data.documentUrl, '_blank')
}
})
} else {
ElMessage.error('单号格式错误,请检查输入')
}
}
// 监听路由变化,自动添加标签
watch(
() => route.path,
......@@ -591,24 +412,6 @@ watch(
{ immediate: true },
)
watch(getLogisticDrawer, async (value: boolean) => {
if (value) {
initPrintDevice()
const locaclPrinter = localStorage.getItem('sheetPrinter')
if (locaclPrinter) {
const savedPrinter = JSON.parse(locaclPrinter)
// 检查保存的打印机是否在当前打印机列表中
if (printDeviceList.value.includes(savedPrinter)) {
sheetPrinter.value = savedPrinter
} else {
// 如果保存的打印机不在当前列表中,清空 localStorage 并设置为空
localStorage.removeItem('sheetPrinter')
sheetPrinter.value = ''
}
}
}
})
// 点击标签时切换路由
watch(activeTab, (newTab) => {
if (newTab) {
......@@ -741,38 +544,4 @@ onUnmounted(() => {
display: flex;
align-items: center;
}
.tool_warper {
position: absolute;
top: 50px;
right: 0;
bottom: 0;
width: 40px;
background: #001529;
z-index: 999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
::v-deep(.el-drawer.rtl) {
height: 650px !important;
right: 40px;
top: calc(50% - 325px) !important;
}
::v-deep(.get-logistic-drawer.el-drawer.btt) {
height: 120px !important;
width: 700px !important;
top: calc(50% - 60px) !important;
left: calc(50% - 350px) !important;
}
.tool-item {
width: 24px;
height: 24px;
cursor: pointer;
margin-bottom: 10px;
}
</style>
<template>
<!-- 右侧工具栏 -->
<div class="tool_warper">
<div title="格式工具" class="tool-item" @click="formatDrawer = true">
<img src="../assets/images/brush-no.png" width="24" height="24" />
</div>
<div
title="打印物流面单"
class="tool-item"
@click="getLogisticDrawer = true"
>
<img src="../assets/images/printer.png" width="24" height="24" />
</div>
</div>
<!-- 格式工具 -->
<span>
<el-drawer
v-model="formatDrawer"
class="format-drawer"
title="格式工具"
direction="rtl"
size="45%"
>
<el-input
v-model="textareaT"
style="width: 100%"
:rows="10"
type="textarea"
placeholder="请输入"
@input="confimTools"
/>
<div
style="
margin-top: 10px;
height: 250px;
overflow: auto;
border: 1px solid #ddd;
padding: 10px;
"
>
{{ newTextareaT }}
</div>
<template #footer>
<span style="display: flex; justify-content: center">
<el-button @click="formatDrawer = false"> 取 消 </el-button>
<el-button type="primary" @click="copy"> 拷 贝 </el-button>
</span>
</template>
</el-drawer>
</span>
<!-- 打印物流面单 -->
<span>
<el-dialog
v-model="getLogisticDrawer"
class="get-logistic-dialog"
title="打印物流面单"
width="1200px"
:before-close="
() => {
getLogisticDrawer = false
content = ''
tableData = {}
productCount = null
}
"
>
<ElSelect
v-model="sheetPrinter"
placeholder="请选择打印机"
style="width: 200px; margin-right: 10px"
@change="handlePrinterChange"
>
<ElOption
v-for="item in printDeviceList"
:key="item"
:label="item"
:value="item"
/>
</ElSelect>
<el-input
v-model="content"
placeholder="请输入单号"
style="width: 700px; margin-right: 10px"
clearable
@keyup.enter="getLogistic"
/>
<el-button type="primary" @click="getLogistic">查 询</el-button>
<el-button type="success" @click="printLogistic">打印面单</el-button>
<div style="font-size: 20px; margin: 20px 0px; color: #666666">
<div style="margin-bottom: 10px; display: flex; gap: 20px">
<div style="width: 400px">店铺单号: {{ tableData.shopNumber }}</div>
<div>
包含产品数:
<span style="color: red">{{ tableData.purchaseNumber }}</span>
</div>
</div>
<div>物流跟踪号: {{ tableData.trackingNumber }}</div>
</div>
<TableView :columns="tableColumns" :data="tableData.productList">
<template #warehouseSkuImage="scope">
<span
style="
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
"
>
<img
:src="scope.row.warehouseSkuImage"
alt="商品图片"
height="50"
style="cursor: pointer"
@click="handlePictureCardPreview(scope.row.warehouseSkuImage)"
/>
<span
class="mark-tag"
:title="handleMark(scope.row.productMark).label"
:style="{
background: handleMark(scope.row.productMark).color,
width: handleMark(scope.row.productMark).width || '20px',
}"
>
{{ handleMark(scope.row.productMark).name }}
</span>
</span>
</template>
</TableView>
</el-dialog>
</span>
<!-- 预览图片 -->
<el-dialog v-model="dialogVisible" width="35%">
<img :src="dialogImageUrl" alt="商品预览图片" />
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, watch, computed } from 'vue'
import { ElMessage } from 'element-plus'
import useLodop from '@/utils/hooks/useLodop'
import { getLogisticUSApi, getLogisticCNApi } from '@/api/common'
import { filePath, FileCnPath } from '@/api/axios'
import { LogisticBill } from '@/types/api/podMakeOrder'
import TableView from '@/components/TableView.vue'
const formatDrawer = ref(false)
const getLogisticDrawer = ref(false)
const textareaT = ref('')
const newTextareaT = ref('')
const content = ref('')
const printDeviceList = ref<string[]>([])
const sheetPrinter = ref('')
// 表格列配置
const tableColumns = computed(() => [
{
label: '图片',
prop: 'warehouseSkuImage',
align: 'center',
width: 150,
slot: 'warehouseSkuImage',
},
{
label: '商品sku',
prop: 'warehouseSku',
align: 'center',
},
{
label: '店铺sku',
prop: 'shopSku',
align: 'center',
},
{
label: 'sku名称',
prop: 'warehouseSkuName',
align: 'center',
},
{
label: '数量',
prop: 'purchaseNumber',
align: 'center',
width: 80,
},
{
label: '包裹号',
prop: 'orderItemId',
align: 'center',
},
{
label: '生产单号',
prop: 'podJomallNo',
align: 'center',
},
])
// 商品标记
const handleMark = (v: string) => {
switch (v) {
case 'virtual':
return {
name: 'VIR',
color: '#ff9900',
label: '虚拟商品',
}
case 'normal':
return {
name: 'G',
color: '#67C23A',
label: '普通商品',
}
case 'pod':
return {
name: 'P',
color: '#F56C6C',
label: 'pod商品',
}
case 'custom':
return {
name: 'C',
color: '#6d9eeb',
label: '一件定制商品',
}
case 'custom_part':
return {
name: 'CP',
color: '#6d9eeb',
label: '一件定制局部印商品',
}
case 'custom_full':
return {
name: 'CF',
color: '#6d9eeb',
label: '一件定制满印商品',
}
default:
return {
name: '',
color: '#FFFFFF',
width: '0',
}
}
}
// 格式工具输入格式化
const confimTools = (v: string) => {
// 先处理结尾的多个空格,只保留一个
let text = v.replace(/\s+$/, ' ')
// 处理开头的多个空格,只保留一个
text = text.replace(/^\s+/, ' ')
// 将多个连续空格替换为单个空格
text = text.replace(/\s+/g, ' ')
// 然后将所有空格和换行符替换为逗号
text = text.replace(/(\s|[\r\n])/g, ',')
// 如果结尾是逗号,则删除
if (text.slice(-1) === ',') {
newTextareaT.value = text.slice(0, -1)
} else {
newTextareaT.value = text
}
}
// 复制
const copy = () => {
const oInput = document.createElement('input')
oInput.value = newTextareaT.value
document.body.appendChild(oInput)
oInput.select() // 选择对象
document.execCommand('Copy') // 执行浏览器复制命令
oInput.className = 'oInput'
oInput.style.display = 'none'
document.body.removeChild(oInput)
ElMessage({
message: '复制成功',
type: 'success',
})
}
const dialogImageUrl = ref('')
const dialogVisible = ref(false)
// 放大图片
const handlePictureCardPreview = (fileUrl: string) => {
dialogImageUrl.value = fileUrl
dialogVisible.value = true
}
const { getCLodop } = useLodop()
// 初始化打印机
const initPrintDevice = () => {
const lodop = getCLodop(null, null)
if (!lodop) {
printDeviceList.value = []
return
}
try {
const arr = []
// 获取打印机数量
const length = lodop.GET_PRINTER_COUNT()
for (let i = 0; i < length; i++) {
// 根据设备序号获取设备名
const name = lodop.GET_PRINTER_NAME(i)
if (name) {
arr.push(name)
}
}
// 获取默认打印机
const defaultPrinter = lodop.GET_PRINTER_NAME(0)
if (defaultPrinter) {
sheetPrinter.value = defaultPrinter
}
printDeviceList.value = arr
} catch (error) {
console.error('初始化打印机失败:', error)
printDeviceList.value = []
}
}
// 处理打印机选择变化,保存到 localStorage
const handlePrinterChange = (value: string) => {
sheetPrinter.value = value
localStorage.setItem('sheetPrinter', JSON.stringify(value))
}
const tableData = ref<LogisticBill>({})
const productCount = ref<number | null>(null)
const type = ref('')
// 查询并打印物流面单
const getLogistic = () => {
content.value = content.value.trim()
if (!content.value) {
ElMessage.warning('请输入单号')
return
}
// 按_分割字符串
const parts = content.value.split('_')
// 检查是否有足够的_分隔符
if (parts.length < 4) {
ElMessage.error('单号格式错误,请检查输入')
return
}
// 获取第三个_后面的内容
type.value = parts[3]
if (type.value.startsWith('USPSC')) {
// 美国物流
getLogisticUSApi(content.value).then((res) => {
if (res.code === 200) {
tableData.value = res.data
productCount.value = tableData.value.productList?.length || 0
window.open(filePath + tableData.value.expressSheet, '_blank')
}
})
} else if (type.value.startsWith('JMPSC')) {
// 中国物流
getLogisticCNApi(content.value).then((res) => {
if (res.code === 200) {
tableData.value = res.data
productCount.value = tableData.value.productList?.length || 0
window.open(FileCnPath + tableData.value.expressSheet, '_blank')
}
})
} else {
ElMessage.error('单号格式错误,请检查输入')
}
}
// 手动打印面单
const printLogistic = () => {
if (!tableData.value.expressSheet) {
ElMessage.warning('请先查询面单')
return
}
if (type.value.startsWith('USPSC')) {
window.open(filePath + tableData.value.expressSheet, '_blank')
} else if (type.value.startsWith('JMPSC')) {
window.open(FileCnPath + tableData.value.expressSheet, '_blank')
}
}
// 监听打印机状态
watch(getLogisticDrawer, async (value: boolean) => {
if (value) {
initPrintDevice()
const locaclPrinter = localStorage.getItem('sheetPrinter')
if (
locaclPrinter &&
Array.isArray(printDeviceList.value) &&
printDeviceList.value.length > 0
) {
try {
const savedPrinter = JSON.parse(locaclPrinter)
// 检查保存的打印机是否在当前打印机列表中
if (printDeviceList.value.includes(savedPrinter)) {
sheetPrinter.value = savedPrinter
} else {
// 如果保存的打印机不在当前列表中,清空 localStorage 并设置为空
localStorage.removeItem('sheetPrinter')
sheetPrinter.value = ''
}
} catch (error) {
console.error('解析保存的打印机信息失败:', error)
localStorage.removeItem('sheetPrinter')
sheetPrinter.value = ''
}
}
}
})
</script>
<style lang="scss" scoped>
.tool_warper {
position: absolute;
top: 50px;
right: 0;
bottom: 0;
width: 40px;
background: #001529;
z-index: 999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
// 格式工具
::v-deep(.format-drawer.el-drawer.rtl) {
height: 650px !important;
right: 40px;
top: calc(50% - 325px) !important;
}
// 打印物流面单
::v-deep(.el-dialog.get-logistic-dialog) {
height: 700px !important;
padding: 30px;
}
.tool-item {
width: 24px;
height: 24px;
cursor: pointer;
margin-bottom: 10px;
}
.mark-tag {
display: inline-block;
background: #f56c6c;
width: 20px;
height: 20px;
border-radius: 11px;
line-height: 20px;
color: white;
}
</style>
......@@ -7,13 +7,18 @@ import './styles/index.scss'
import VxeUITable from 'vxe-table'
import 'vxe-table/lib/style.css'
import { isPermissionBtn } from '@/utils/index'
// 确保在渲染用户提供的HTML内容时,不会执行任何潜在的恶意脚本,从而提高应用的安全性
import vueDomPurifyHTMLPlugin from 'vue-dompurify-html'
createApp(App)
const app = createApp(App)
app
.use(vueDomPurifyHTMLPlugin)
.use(router)
.use(store)
.use(VxeUITable)
.mount('#app')
app.config.globalProperties.$isPermissionBtn = isPermissionBtn
......@@ -263,6 +263,13 @@ const router = createRouter({
},
component: WarehousePosition,
},
{
path: '/setting/settingIndex',
meta: {
title: '设置',
},
component: () => import('@/views/setting/settingIndex.vue'),
},
],
},
// 登录
......
......@@ -198,6 +198,11 @@ const menu: MenuItem[] = [
},
],
},
{
index: '/setting/settingIndex',
id: 7,
label: '设置',
},
// {
// index: '',
......
import { defineStore } from 'pinia'
const usePermissionBtnStore = defineStore('permissionBtn', {
state: () => ({
permissionBtns: [] as string[], // 权限按钮数组
}),
actions: {
setBtn(state: string[]) {
this.permissionBtns = state
},
},
})
export default usePermissionBtnStore
......@@ -30,8 +30,15 @@ const useUserStore = defineStore('user', {
async login(data: LoginReq) {
try {
const resp = await loginApi(data)
console.log(33, resp)
this.setUser(resp.data.sysUser)
setToken(resp.data.token)
localStorage.setItem(
'baseExternalAccountTypes',
JSON.stringify(resp.data.sysUser.baseExternalAccountTypes),
)
router.push({ path: '/dashboard' })
} catch (error) {
// showError(error)
......
......@@ -24,6 +24,7 @@ export interface SysUser {
factoryCode: string
status: number
factory: Factory
baseExternalAccountTypes?: []
}
export interface Factory {
......
......@@ -16,4 +16,5 @@ export interface ExternalAuthListData {
token?: string
refreshToken?: string
refreshTokenFailureTime?: string
enable?: boolean
}
......@@ -48,3 +48,22 @@ export interface ProductList {
podJomallCnNo?: string
previewImgs?: { sort: string | number; title: string; url: string }[]
}
export interface LogisticBill {
expressSheet?: string
salesPlatform?: string
orderStatus?: string
shopWay?: string
blocked?: boolean
logisticsCompanyName?: string
logisticsWayNameId?: string
shopNumber?: string
id?: string
shopId?: string
orderId?: string
orderNumber?: string
trackingNumber?: string
systemWeight?: number | string
purchaseNumber?: number
productList?: ProductList[]
}
import { get } from 'lodash-es'
import { ElMessage } from 'element-plus'
import { DateTime } from 'luxon'
import usePermissionBtnStore from '@/store/permission'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function val<T>(data: T, key: string | ((data: T) => any)) {
if (typeof key === 'function') return key(data)
......@@ -40,3 +42,10 @@ export function convertToChinaTime(
const chinaTime = inputTime.setZone(targetZone)
return chinaTime.toFormat('yyyy-MM-dd HH:mm:ss')
}
export function isPermissionBtn(key: string) {
const permissionBtns = usePermissionBtnStore().permissionBtns
if (!permissionBtns) return false
return permissionBtns.includes(key)
}
......@@ -81,6 +81,7 @@ import {
getLogisticsLog,
getLogisticsCompanyList,
getUniuniList,
getTiktokCarrier,
} from '@/api/logistics'
import { WarehouseListData } from '@/types/api/podUsOrder'
......@@ -401,6 +402,36 @@ const formConfig = computed<IFormConfig[]>(() => [
))}
</el-select>
</el-form-item>
{item['showPlatform'].length === 1 &&
item['showPlatform'][0] === 'TIKTOK' ? (
<el-form-item
key={index}
class="renderItem"
label="物流名称"
style="display: flex;flex:50%"
prop={`platformList.${index}.logisticsName`}
rules={[
{
required: true,
message: '请输入物流名称',
trigger: 'blur',
},
]}
>
<el-select
v-model={item.logisticsName}
placeholder="请选择物流名称"
>
{tiktokCarriers.value?.map((el) => (
<el-option
label={el.name}
value={el.name}
key={el.id}
></el-option>
))}{' '}
</el-select>
</el-form-item>
) : (
<el-form-item
key={index}
class="renderItem"
......@@ -420,6 +451,7 @@ const formConfig = computed<IFormConfig[]>(() => [
placeholder="请输入物流名称"
/>
</el-form-item>
)}
<div style="display: flex;flex:17%">
{(formData?.platformList as platformObj[])?.length - 1 ===
index && (
......@@ -575,6 +607,7 @@ const tableConfig = ref<TableColumn[]>([
onMounted(() => {
getAllList()
getTiktokCarriers()
})
/**
......@@ -835,6 +868,15 @@ async function getAllList() {
}
}
const tiktokCarriers = ref<{ name: string; id: number }[]>([])
/**
* @description: 获取tictok物流承运商
*/
async function getTiktokCarriers() {
const { data } = await getTiktokCarrier()
tiktokCarriers.value = data
}
/**
* @description: 日志弹窗
*/
......
......@@ -641,7 +641,9 @@ const getOrderByIdApi = async (type: string) => {
}
const tempChinaLogs = ref<LogListData[]>([])
const zoneType = ref<string>('Asia/Shanghai')
function changeChinaTime(zone: string) {
zoneType.value = zone
logList.value = tempChinaLogs.value.map((el) => {
return {
...el,
......@@ -1033,24 +1035,28 @@ onMounted(() => {
<el-tab-pane label="操作日志" @tab-click="operationLog">
<div>
<el-button
type="primary"
:type="zoneType === 'Asia/Shanghai' ? 'primary' : ''"
size="small"
link
style="margin-left: 10px"
@click="changeChinaTime('Asia/Shanghai')"
>转换为中国时间</el-button
>北京时间</el-button
>
<el-button
type="primary"
:type="zoneType === 'America/New_York' ? 'primary' : ''"
size="small"
link
style="margin-left: 10px"
@click="changeChinaTime('America/New_York')"
>转换为新泽西时间</el-button
>新泽西时间</el-button
>
<el-button
type="primary"
:type="zoneType === 'America/Los_Angeles' ? 'primary' : ''"
size="small"
link
style="margin-left: 10px"
@click="changeChinaTime('America/Los_Angeles')"
>转换为洛杉矶时间</el-button
>洛杉矶时间</el-button
>
</div>
......
......@@ -50,7 +50,7 @@
v-model="searchForm.craftCode"
clearable
filterable
placeholder="请输入工艺"
placeholder="请选择工艺"
style="width: 150px"
>
<el-option
......@@ -1113,7 +1113,6 @@
</div>
<div
v-if="
row.productionClient === 'JOMALL' &&
row.status !== 'TO_BE_CONFIRMED'
"
class="order-detail-item"
......@@ -1126,21 +1125,6 @@
{{ row.warehouseName }}
</span>
</div>
<div
v-if="
row.productionClient === 'JOMALL' &&
row.status !== 'TO_BE_CONFIRMED'
"
class="order-detail-item"
>
<span class="order-detail-item-label">物流方式:</span>
<span
class="order-detail-item-value"
:title="row.logisticsWayName"
>
{{ row.logisticsWayName }}
</span>
</div>
<div class="order-detail-item">
<span class="order-detail-item-label">收货人:</span>
<span class="order-detail-item-value" :title="row.receiverName">
......@@ -1857,7 +1841,7 @@
v-model="confirmManuShow"
:close-on-click-modal="false"
title="确认生产"
width="40%"
width="500px"
>
<el-form
ref="confirmFormRef"
......@@ -1865,12 +1849,12 @@
label-width="120px"
:rules="confirmRules"
>
<el-form-item label="仓库" prop="warehouseId">
<el-form-item label="发货仓库" prop="warehouseId">
<ElSelect
v-model="confirmForm.warehouseId"
clearable
filterable
placeholder="请选择仓库"
placeholder="请选择发货仓库"
style="width: 80%"
>
<el-option
......@@ -3215,7 +3199,7 @@ const confirmRules = reactive({
warehouseId: [
{
required: true,
message: '请选择仓库',
message: '请选择发货仓库',
trigger: 'change',
},
],
......
......@@ -552,28 +552,6 @@ const trackCodeInput = async () => {
removeFromHistory()
return ElMessage.error('生产单不存在')
}
if (res.data.interceptStatus == 0 || res.data.interceptStatus == 2) {
try {
await ElMessageBox.confirm(
'该生产单已提交至拦截申请,请确认是否继续生产',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
},
)
} catch (error) {
TrackingNumber.value = ''
detail.value = {
id: -1,
podJomallOrderUsId: -1,
imgList: [],
}
trackingNumberRef.value && trackingNumberRef.value.focus()
return
}
}
const d = JSON.parse(JSON.stringify(res.data))
if (d.note) {
......
......@@ -37,6 +37,7 @@
</ElSelect>
</div>
<div class="form-item">
<ElSelect
v-model="warehouseId"
placeholder="请选择仓库"
......@@ -368,6 +369,10 @@ watch(visible, async (value: boolean) => {
_warehouseId.value = locaclWarehouseId
? JSON.parse(locaclWarehouseId)
: props.warehouseList[0].id
_warehouseId.value = locaclWarehouseId
? JSON.parse(locaclWarehouseId)
: props.warehouseList[0].id
if (userStore.user?.factory.id) {
try {
await socket.init(
......
......@@ -2,7 +2,7 @@
<el-dialog
title="处理结果"
v-model="resultDialog"
width="600px"
width="1200px"
:close-on-click-modal="false"
@closed="closedFn"
>
......@@ -33,8 +33,17 @@
<div style="margin: 15px 0"></div>
<el-checkbox-group v-model="selectedList" @change="checkChange">
<div style="display: block" v-for="(item, index) in list" :key="index">
<el-checkbox :value="item">
{{ '工厂订单号:' + item.factoryOrderNumber + ' ' + item.message }}
<span
style="
display: inline-block;
width: 100%;
white-space: wrap;
line-height: 22px;
"
> <span>订单号:{{item.factoryOrderNumber}},</span>{{ item.message }}</span
>
</el-checkbox>
</div>
</el-checkbox-group>
......
<script setup lang="ts">
import { defineModel } from 'vue'
import { updateAddressApi } from '@/api/podUsOrder.ts'
import {AddressInfo} from '@/types/api/podUsOrder.ts'
import { AddressInfo } from '@/types/api/podUsOrder.ts'
const emits = defineEmits(['success'])
defineProps<{
......@@ -117,6 +117,8 @@ const submitForm = async () => {
v-model="form.receiverAddress1"
clearable
placeholder="请输入地址1"
maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item label="地址2" prop="receiverAddress2">
......@@ -124,6 +126,8 @@ const submitForm = async () => {
v-model="form.receiverAddress2"
clearable
placeholder="请输入地址2"
maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item label="邮政编码" prop="receiverPostCode">
......
......@@ -262,7 +262,33 @@
<ElButton type="primary" @click="search">查询</ElButton>
</span>
</ElFormItem>
<!-- <ElFormItem
v-if="
status === 'PICKING' ||
status === 'TO_BE_REPLENISHMENT' ||
status === 'IN_PRODUCTION'
"
>
<ElDropdown>
<el-button type="primary">
DTF排版<el-icon class="el-icon--right"><ArrowDown /></el-icon>
</el-button>
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem
:loading="tifDownloadLoading"
@click="downloadTif('png')"
>TIF排版</ElDropdownItem
>
<ElDropdownItem
:loading="pngDownloadLoading"
@click="downloadTif('png')"
>PNG排版</ElDropdownItem
>
</ElDropdownMenu></template
></ElDropdown
></ElFormItem
> -->
<ElFormItem
v-if="
status === 'PICKING' ||
......@@ -381,7 +407,9 @@
</ElButton>
</span>
</ElFormItem>
<ElFormItem v-if="status === 'TO_BE_CONFIRMED'">
<ElFormItem
v-if="status === 'TO_BE_CONFIRMED' && isPermissionBtn('RIIN')"
>
<span class="item">
<ElButton type="success" @click="confirmProductToRiin">
转至锐印生产
......@@ -519,6 +547,7 @@
<ElButton type="warning" @click="arrangeFinish">排单完成</ElButton>
</span>
</ElFormItem>
<ElFormItem
v-if="status !== 'BATCH_DOWNLOAD' && status !== 'WAIT_SHIPMENT'"
>
......@@ -630,7 +659,9 @@
</ElDropdown>
</span>
</ElFormItem>
<ElFormItem v-if="status === 'COMPLETE'">
<ElFormItem
v-if="['WAIT_TRACK', 'COMPLETE', 'IN_TRANSIT'].includes(status)"
>
<span class="item">
<ElButton type="warning" @click="statusPush">状态推送</ElButton>
</span>
......@@ -1434,7 +1465,7 @@
</span>
<span class="operate-item">
<ElButton
:disabled="!row.tiffUrl"
disabled
link
type="primary"
@click="handleDownload(row, 'tiff')"
......@@ -1445,6 +1476,24 @@
<span class="operate-item">
<ElButton
link
type="primary"
@click="printPickingOrderItem(row, 1)"
>
打印拣货单
</ElButton>
</span>
<span class="operate-item">
<ElButton
link
type="primary"
@click="printPickingOrderItem(row, 2)"
>
打印生产单
</ElButton>
</span>
<span class="operate-item">
<ElButton
link
type="warning"
:loading="reComposingLoadingMap[row.id]"
@click="handleReComposingDesign(row)"
......@@ -1530,6 +1579,17 @@
刷新地址
</ElButton>
</span>
<span
v-if="
(status === 'IN_TRANSIT' || status === 'COMPLETE') &&
isPermissionBtn('TRACK')
"
class="operate-item"
>
<ElButton link type="primary" @click="logTrajectory(row)"
>物流轨迹</ElButton
>
</span>
</div>
</template>
</TableView>
......@@ -1574,8 +1634,8 @@
</el-tooltip>
</template>
<template
#top_right
v-if="['ZPZY', 'CXZY', 'THZY'].includes(cardItem.craftCode as string)"
#top_right
>
<img
:src="`/images/pic/${cardItem.craftCode}.png`"
......@@ -1595,26 +1655,9 @@
>
{{ cardItem?.factorySubOrderNumber }}
</span>
<el-icon
class="icon"
style="margin-left: 3px"
size="14"
@click="copy(cardItem?.factorySubOrderNumber || '')"
>
<DocumentCopy />
</el-icon>
</template>
<template #operations>
<div
style="position: absolute; top: -35px; right: 2px"
v-if="cardItem.platform"
>
<img
:src="`/images/icon/${getPlatformImg(cardItem.platform)}`"
style="height: 30px"
/>
</div>
<div
v-if="cardItem?.customizedQuantity"
class="customizedQuantity"
:title="`类型:${getQuantityText(
......@@ -1655,6 +1698,12 @@
/>
</div>
</div>
<div v-if="cardItem.platform">
<img
:src="`/images/icon/${getPlatformImg(cardItem.platform)}`"
style="height: 30px"
/>
</div>
</div>
</template>
<template #info>
......@@ -1688,20 +1737,15 @@
}}
</span>
</div>
<div class="grid-item">
<div
class="grid-item"
@click="copy(cardItem?.variantSku || '')"
>
<div
:title="`variant SKU: ${cardItem?.variantSku || ''}`"
class="grid-item-value"
>
<div class="orderNumber">{{ cardItem?.variantSku }}</div>
<el-icon
class="icon"
style="margin-left: 3px"
size="14"
@click="copy(cardItem?.variantSku || '')"
>
<DocumentCopy />
</el-icon>
</div>
</div>
<div v-if="status !== 'TO_BE_ARRANGE'" class="grid-item">
......@@ -1710,12 +1754,19 @@
{{ cardItem?.replenishmentNum || 0 }}
</span>
</div>
<!-- <div class="grid-item">
<span title="Variant SKU" class="grid-item-value">
{{ cardItem?.variantSku }}
</span>
</div> -->
<div
class="grid-item"
@click="copy(cardItem?.thirdSkuCode || '')"
>
<div
:title="`库存SKU:${cardItem?.thirdSkuCode || ''}`"
class="grid-item-value"
>
<div class="orderNumber">
{{ cardItem?.thirdSkuCode }}
</div>
</div>
</div>
<div
class="grid-item"
:title="`工艺:${cardItem?.craftName || ''}`"
......@@ -1725,23 +1776,15 @@
{{ cardItem?.craftName }}
</span>
</div>
<div class="grid-item">
<div
:title="`库存SKU:${cardItem?.thirdSkuCode || ''}`"
class="grid-item-value"
class="grid-item"
:title="`店铺单号:${cardItem?.shopNumber || ''}`"
@click="copy(cardItem?.shopNumber || '')"
>
<div class="grid-item-value">
<div class="orderNumber">
{{ cardItem?.thirdSkuCode }}
{{ cardItem?.shopNumber }}
</div>
<el-icon
class="icon"
style="margin-left: 3px"
size="14"
@click="copy(cardItem?.thirdSkuCode || '')"
>
<DocumentCopy />
</el-icon>
</div>
</div>
<div class="grid-item">
......@@ -1753,6 +1796,17 @@
{{ cardItem?.supplierProductNo }}
</span>
</div>
<div
class="grid-item"
@click="copy(cardItem?.factoryOrderNumber || '')"
>
<div
:title="`订单号:${cardItem?.factoryOrderNumber}`"
class="grid-item-value orderNumber"
>
{{ cardItem?.factoryOrderNumber }}
</div>
</div>
<div class="grid-item">
<span class="grid-item-label">货号:</span>
<span
......@@ -1763,24 +1817,6 @@
</span>
</div>
<div
class="grid-item"
:title="`店铺单号:${cardItem?.shopNumber || ''}`"
>
<div class="grid-item-value">
<div class="orderNumber">
{{ cardItem?.shopNumber }}
</div>
<el-icon
class="icon"
style="margin-left: 3px; flex-shrink: 0"
size="14"
@click="copy(cardItem?.shopNumber || '')"
>
<DocumentCopy />
</el-icon>
</div>
</div>
<!-- <div class="grid-item" title="订单号">
<span
class="grid-item-value"
......@@ -1790,44 +1826,17 @@
</span>
</div> -->
<div class="grid-item">
<div
:title="`订单号:${cardItem?.thirdSubOrderNumber}`"
class="grid-item-value"
>
<div class="orderNumber">
{{ cardItem?.thirdSubOrderNumber }}
</div>
<el-icon
class="icon"
style="margin-left: 3px"
size="14"
@click.stop="
copy(String(cardItem?.thirdSubOrderNumber || ''))
"
>
<DocumentCopy />
</el-icon>
</div>
</div>
<div
v-if="cardItem.batchArrangeNumber"
class="grid-item"
:title="`批次号:${cardItem?.batchArrangeNumber || ''}`"
@click="copy(cardItem?.batchArrangeNumber || '')"
>
<div class="grid-item-value">
<div class="orderNumber">
{{ cardItem?.batchArrangeNumber }}
</div>
</div>
<el-icon
class="icon"
style="margin-left: 3px"
size="14"
@click="copy(cardItem?.batchArrangeNumber || '')"
>
<DocumentCopy />
</el-icon>
</div>
<div v-if="cardItem.isReplenishment" class="grid-item">
<el-tag size="small" type="danger" effect="dark"
......@@ -1977,28 +1986,33 @@
<template #header="{ titleId, titleClass }">
<div style="display: flex" class="my-header">
<div :id="titleId" :class="titleClass">操作日志</div>
<!-- <el-button
type="primary"
<div style="margin-left: 15px">
<el-button
:type="timeType === 'Asia/Shanghai' ? 'primary' : ''"
size="small"
link
style="margin-left: 10px"
@click="changeChinaTime"
>转换为中国时间</el-button
> -->
@click="changeChinaTime('Asia/Shanghai')"
>北京时间</el-button
>
<el-button
type="primary"
:type="timeType === 'America/New_York' ? 'primary' : ''"
size="small"
link
style="margin-left: 10px"
@click="changeChinaTime('America/New_York')"
>转换为新泽西时间</el-button
>新泽西时间</el-button
>
<el-button
type="primary"
:type="timeType === 'America/Los_Angeles' ? 'primary' : ''"
size="small"
link
style="margin-left: 10px"
@click="changeChinaTime('America/Los_Angeles')"
>转换为洛杉矶时间</el-button
>洛杉矶时间</el-button
>
</div>
</div>
</template>
<LogList :log-list="logList" />
</el-dialog>
......@@ -2281,6 +2295,21 @@
<el-dialog v-model="dialogVisible" width="35%">
<img :src="dialogImageUrl" alt="商品预览图片" />
</el-dialog>
<el-dialog title="物流轨迹" v-model="timeLineVisible" width="1000px">
<el-timeline>
<el-timeline-item
v-for="(item, index) in timeLine"
:key="index"
:timestamp="item.time_utc"
placement="top"
:type="index == 0 ? 'primary' : ''"
>
<el-card>
{{ item.description }}
</el-card>
</el-timeline-item>
</el-timeline>
</el-dialog>
</template>
<script setup lang="ts">
import { getUserMarkList } from '@/api/common'
......@@ -2348,6 +2377,10 @@ import {
rejectToApi,
statusPushApi,
completeDeliveryApi,
get17TrackInfoApi,
getAccountCodeByFactoryIdApi,
printPickPdfByBatchNumberApi,
printProductionPdfByBatchNumberApi,
} from '@/api/podUsOrder'
import { BaseRespData } from '@/types/api'
......@@ -2370,6 +2403,7 @@ import {
LogisticsFormData,
CraftListData,
ExportParams,
InterceptStateGroupData,
} from '@/types/api/podUsOrder'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue'
......@@ -2388,6 +2422,9 @@ import RightClickMenu from '@/components/RightClickMenu.vue'
import ResultInfo from './components/ResultInfo.vue'
import { isArray, isString } from '@/utils/validate'
import platformJson from '../../../json/platform.json'
import { getToken } from '@/api/axios'
import usePermissionBtnStore from '@/store/permission'
const permissionBtns = usePermissionBtnStore()
import {
useRouter,
type NavigationGuardNext,
......@@ -2532,6 +2569,7 @@ const interceptCurrent = ref(1)
const interceptStatus = ref(0)
const userMarkList = ref<string[]>([])
const selection = ref<PodUsOrderListData[]>([])
const currentItem = ref<PodUsOrderListData | null>(null)
const pickerOptions = {
shortcuts: [
{
......@@ -2781,12 +2819,12 @@ const tableColumns = computed(() => {
{
label: '订单数量',
prop: 'productNum',
width: 100,
width: 90,
align: 'center',
},
{
label: '素材数量',
width: 120,
width: 90,
prop: 'materialNum',
align: 'center',
},
......@@ -2798,21 +2836,21 @@ const tableColumns = computed(() => {
},
{
label: '失败原因',
minWidth: 300,
minWidth: 250,
prop: 'failReason',
slot: 'failReason',
align: 'left',
},
{
label: '创建时间',
width: 200,
width: 180,
prop: 'createTime',
slot: 'createTime',
align: 'center',
},
{
label: '完成时间',
width: 200,
width: 180,
prop: 'finishTime',
slot: 'finishTime',
align: 'center',
......@@ -2827,7 +2865,7 @@ const tableColumns = computed(() => {
{
label: '操作',
slot: 'operate',
width: 300,
width: 450,
align: 'center',
fixed: 'right',
prop: 'operate',
......@@ -2996,19 +3034,18 @@ const loadTabData = async () => {
)
// 发货拦截数量
const shipmentCount = (statusRes.data as any)?.shipment
? Object.values((statusRes.data as any).shipment).reduce(
(sum: number, value: any) => sum + (Number(value) || 0),
0,
)
const shipmentCount = (statusRes.data as InterceptStateGroupData)?.shipment
? Object.values(
(statusRes.data as InterceptStateGroupData).shipment,
).reduce((sum: number, value: unknown) => sum + (Number(value) || 0), 0)
: 0
// 生产拦截数量
const productionCount = (statusRes.data as any)?.production
? Object.values((statusRes.data as any).production).reduce(
(sum: number, value: any) => sum + (Number(value) || 0),
0,
)
const productionCount = (statusRes.data as InterceptStateGroupData)
?.production
? Object.values(
(statusRes.data as InterceptStateGroupData).production,
).reduce((sum: number, value: unknown) => sum + (Number(value) || 0), 0)
: 0
tabsNav.value.splice(completeIndex + 1, 0, {
......@@ -3542,6 +3579,39 @@ const printPickingOrder = async () => {
loading.close()
}
}
const printPickingOrderItem = async (
row: { batchArrangeNum: string },
type: number,
) => {
console.log(333, row)
const loading = ElLoading.service({
fullscreen: true,
text: '操作中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
let res
if (type === 1) {
res = await printPickPdfByBatchNumberApi({
batchArrangeNumber: row.batchArrangeNum,
})
} else if (type === 2) {
res = await printProductionPdfByBatchNumberApi({
batchArrangeNumber: row.batchArrangeNum,
})
}
if (res?.code !== 200) return
ElMessage.success('操作成功')
window.open(filePath + res?.message)
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const pickingComplete = async () => {
if (cardSelection.value.length === 0) {
return ElMessage.warning('请选择数据')
......@@ -3855,6 +3925,24 @@ const arrangeFinish = async () => {
loading.close()
}
}
interface timeLineType {
time_iso?: string
time_utc?: string
description?: string
}
const timeLineVisible = ref(false)
const timeLine = ref<timeLineType[]>([])
const logTrajectory = async (row: { id: string }) => {
const { data } = await get17TrackInfoApi({ id: row.id })
const res = data.accepted[0]?.track_info?.tracking
timeLine.value = res?.providers[0]?.events
// if (data.rejected?.length) {
// console.log(data.rejected[0].error.message)
// throw Error(data.rejected[0].error.message)
// }
timeLineVisible.value = true
}
const logList = ref<LogListData[]>([])
const logVisible = ref(false)
const operationLog = async (id: number, e: MouseEvent | null) => {
......@@ -4024,6 +4112,7 @@ const rejectOrder = async (type: string) => {
}
const handleStockOut = async (row: PodUsOrderListData) => {
currentItem.value = row
wayDialogTitle.value = `切换物流(当前物流方式:${row.logisticsWayName})`
isChangeWay.value = true
......@@ -4105,8 +4194,8 @@ const changeWaySubmit = async (title: string) => {
try {
const params = {
updateByIdParam: {
id: selection.value[0]?.id,
dataVersion: selection.value[0]?.version as number,
id: currentItem.value?.id || '',
dataVersion: currentItem.value?.version as number,
},
logisticsTrialCalculation: { ...changeWayRow.value },
}
......@@ -4963,16 +5052,29 @@ const completeDelivery = async () => {
}
const tempChinaLogs = ref<LogListData[]>([])
const timeType = ref<string>('Asia/Shanghai')
function changeChinaTime(zone: string) {
timeType.value = zone
logList.value = tempChinaLogs.value.map((el) => {
return {
...el,
createTime: convertToChinaTime(el.createTime, 'Asia/Shanghai', zone),
local: zone.split('/')[1],
local: zone.split('/')[1] === 'Shanghai' ? 'BeiJing' : zone.split('/')[1],
}
})
}
const token = getToken() as string
async function getPermission() {
const res = await getAccountCodeByFactoryIdApi({ token })
permissionBtns.setBtn(res.data)
console.log(res)
}
getPermission()
const globalProperties =
getCurrentInstance()?.appContext.config.globalProperties // 获取全局挂载
const isPermissionBtn = globalProperties?.$isPermissionBtn
useRouter().beforeEach((to, from, next) => {
handleBeforeRouteLeave(to, from, next)
})
......@@ -5119,6 +5221,8 @@ useRouter().beforeEach((to, from, next) => {
.images-position {
display: flex;
gap: 5px;
flex: 1;
overflow-x: auto;
.item-image {
cursor: pointer;
......@@ -5319,6 +5423,16 @@ useRouter().beforeEach((to, from, next) => {
}
.el-timeline-item__wrapper {
padding-left: 15px;
top: -6px;
top: -4px;
}
.el-timeline
> .el-timeline-item:first-child
.el-timeline-item__timestamp.is-top {
color: #409eff;
}
.flex-between {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
<template>
<div class="h100">
<div class="wraper" @mouseout="closeShow">
<div class="image-box" :title="data.name">
<el-image
style="width: 100%; height: 100%"
:src="imageUrl"
fit="cover"
/>
<div
v-if="isShowHomeSku === 'bottom'"
class="app-btn-bottom"
style="width: 100%"
>
<transition>
<div
class="more_wrap"
style="max-width: 100%; overflow-x: auto; overflow-y: hidden"
>
<slot name="operations" />
</div>
</transition>
</div>
</div>
<div class="content">
<div class="colors_main">
<div class="colors_wrap">
<ul class="colors">
<li v-for="(val, index) in data.colorImageList" :key="index">
<img
style="display: block"
width="30"
loading="lazy"
height="30"
:src="val"
alt=""
@mouseenter.stop="setCurrentImg(val)"
/>
</li>
</ul>
</div>
</div>
<!-- <slot name="skuid" v-if="$slots.skuid"></slot> -->
<slot name="content">
<div
style="
display: flex;
align-items: center;
position: relative;
/* height: 24px; */
"
>
<slot name="product-name"></slot>
<slot name="price" />
<slot name="synthesizeStatus"></slot>
<slot name="level-time"></slot>
</div>
</slot>
<slot name="otherContent"></slot>
</div>
<slot name="extra" />
</div>
</div>
</template>
<script lang="ts" setup>
import {computed} from 'vue'
const props = defineProps({
// 控制一些元素显示与否
isStore: {
type: Boolean,
},
width: {
type: Number,
default: 300,
},
hasWidth: {
type: Boolean,
default: false,
},
isFormat: {
type: Boolean,
default: false,
},
smallWidth: {
type: Number,
default: 45,
},
isShowTag: {
type: Boolean,
default: false,
},
isShowHomeSku: {
type: [String, Boolean],
validator: (v) => v === "top" || v === "bottom" || !v,
},
isSkuImageList: {
type: Boolean,
},
isShowSkuId: {
type: Boolean,
default: true,
},
isShowTitle: {
type: Boolean,
default: false,
},
isShowLevel: {
type: Boolean,
default: true,
},
isShowStatus: {
type: Boolean,
default: true,
},
isShowSku: {
type: Boolean,
default: true,
},
isShowSales: {
type: Boolean,
default: true,
},
showSelectable: {
type: Boolean,
default: true,
},
openly: {
type: Number,
default: 1,
},
showOpenly: {
type: Boolean,
default: false,
},
proportion: {
type: Number,
default: 125,
},
data: {
type: Object,
default: () => ({ colorImageList: [] }),
},
selectIds: {
type: Array,
default: () => [],
},
activeFn: {
type: Function,
default: () => {},
},
index: {
type: Number,
default: null,
},
searchForm: {
type: Object,
default: () => {},
},
operational: {
type: Boolean,
default: true,
},
customTagList: {
type: Array,
default: () => [],
},
isCustom: {
type: Boolean,
default: () => false,
},
});
const emits = defineEmits(["setCurrentImg"]);
const imageUrl = computed(() => {
return props.data.img_url;
});
const setCurrentImg = (val: string) => {
if (!val) return;
emits("setCurrentImg", val);
};
const closeShow = () => {
if (!props.data.colorImageList.length) return;
emits("setCurrentImg", props.data.colorImageList[0]);
};
// edit(item) {
// this.$emit('showEdit', item)
// }
</script>
<style lang="scss" scoped>
.wraper {
position: relative;
border-radius: 12px 0px 0px 0px;
border: 1px solid #efefef;
background: #eeeeee;
overflow: hidden;
}
.wraper:hover {
border: 1px solid #4168ff;
}
.addtag {
display: none;
}
.wraper:hover .addtag {
display: block;
}
.sales_wrap {
padding-top: 5px;
height: 24px;
display: flex;
align-items: center;
}
.sales_wrap svg {
margin-bottom: 5px;
}
.sales {
flex: 1;
height: 24px;
margin-left: 5px;
line-height: 24px;
box-sizing: border-box;
background-position: left center;
background-size: contain;
background-repeat: no-repeat;
display: inline-block;
font-size: 14px;
vertical-align: bottom;
padding-top: 1px;
font-weight: bold;
text-align: center;
}
.high {
color: red;
font-weight: bold;
color: #444444;
background: linear-gradient(0deg, rgb(255, 255, 255) 0%, #dddddd 100%);
border-radius: 4px 4px 0 0;
position: relative;
}
.high::before {
position: absolute;
top: -11px;
right: 0;
left: 0;
text-align: center;
line-height: 20px;
font-size: 12px;
font-weight: normal;
}
.today.high::before {
content: "today";
}
.seven.high::before {
content: "seven";
}
.thirty.high::before {
content: "thirty";
}
.history.high::before {
content: "history";
}
.active-icon {
position: relative;
display: block;
width: 16px;
height: 16px;
border: 1px solid #cccccc;
box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.4) inset;
border-radius: 8px;
background: #fff;
}
.active-icon.active {
background: #4168ff;
position: relative;
border-color: #4168ff;
box-shadow: none;
font-size: 18px;
top: -1px;
left: -1px;
}
.active-icon.active::after {
position: absolute;
content: "";
top: 0px;
left: 4px;
width: 4px;
height: 8px;
border-width: 2px;
border-style: solid;
border-color: transparent #fff #fff transparent;
transform: rotate(45deg) scale(0.8);
}
.colors_main {
height: 40px;
display: flex;
align-items: flex-start;
justify-content: space-between;
}
.colors_wrap {
height: 40px;
flex: 1;
overflow-x: auto;
overflow-y: hidden;
}
.colors {
white-space: nowrap;
display: flex;
}
.colors_wrap::-webkit-scrollbar {
height: 5px;
width: 5px;
}
.colors li {
border-right: 1px solid #fff;
width: 30px;
min-width: 30px;
height: 30px;
background: #ddd;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
cursor: pointer;
margin-right: 5px;
border-radius: 5px;
overflow: hidden;
border: 1px solid #ddd;
box-sizing: border-box;
}
.colors li img {
object-fit: contain;
object-position: center;
}
.star {
display: inline-block;
width: 80px;
color: red;
padding-left: 2px;
}
.star span {
display: inline-block;
height: 14px;
width: 14px;
margin-right: 2px;
background-position: center;
background-size: contain;
// background-image: url('../../assets/icon/star-no.svg');
}
// .star span.active {
// background-image: url('../../assets/icon/star.svg');
// }
// .moneyall {
// background-image: url('../../assets/images/moneyall.png');
// }
// .moneyone {
// background-image: url('../../assets/images/moneyone.png');
// }
// .rmb {
// background-image: url('../../assets/images/rmb.png');
// }
.version {
display: block;
width: 18px;
height: 18px;
line-height: 14px;
font-size: 12px;
color: #fff;
padding: 0 0 3px 2px;
text-align: center;
box-sizing: border-box;
// background: no-repeat url('../../assets/images/supply/no-version.png');
}
.repeat {
display: inline-block;
height: 24px;
width: 24px;
border-radius: 12px;
font-size: 12px;
background: red;
line-height: 22px;
color: #fff;
text-align: center;
}
// .version.main {
// background-image: url('../../assets/images/supply/version.png');
// }
// .dollar {
// background-image: url('../../assets/images/supply/money.png');
// }
// .eidt_title {
// background-image: url('../../assets/images/edit001.png');
// }
// .edit_online {
// background-image: url('../../assets/images/plane.png');
// }
.scroll {
overflow: auto;
padding-top: 10px;
user-select: none;
border: 1px solid #ddd;
}
.empty.elements {
border: none;
}
.select-number {
position: absolute;
left: 5px;
top: 10px;
}
.left_mark {
position: absolute;
top: 5px;
left: 5px;
}
.right_check {
position: absolute;
top: 5px;
right: 5px;
display: flex;
}
.right_check > * {
display: block;
margin-bottom: 5px;
}
.app-btn-top {
position: relative;
height: 30px;
}
.app-btn-top > .more_wrap {
position: absolute;
right: 5px;
}
.image-box {
position: relative;
padding-top: 100%;
width: 100%;
.el-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.app-btn-bottom {
position: absolute;
bottom: 5px;
left: 0;
}
}
.log_btn {
height: 22px;
display: inline-block;
background: #ffffff;
margin-right: 5px;
padding: 0 8px;
font-size: 12px;
vertical-align: middle;
box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.2);
border-radius: 13px;
text-align: center;
cursor: pointer;
}
.edit {
vertical-align: middle;
}
.content {
padding: 5px 10px;
background: linear-gradient(
90deg,
rgba(228, 228, 228, 0.4) 0%,
rgba(255, 255, 255, 1) 100%
);
}
.wraper:hover .content {
background: linear-gradient(
90deg,
rgba(26, 125, 255, 0.404) 0%,
rgba(64, 158, 255, 0) 100%
);
}
.footer_wrap {
display: flex;
align-items: center;
padding: 5px 0px 0;
}
.more_wrap {
display: none;
height: 35px;
/* //display: flex; */
align-items: center;
}
.wraper:hover .more_wrap {
display: flex;
}
.more_wrap :deep(> img) {
margin-left: 6px;
cursor: pointer;
}
.more_wrap :deep(> img:hover) {
transform: scale(1.3);
}
.price {
color: rgba(181, 124, 91, 1);
font-size: 14px;
font-weight: bold;
}
.new {
display: block;
width: 43px;
height: 16px;
line-height: 14px;
background: #8fc31f;
text-align: center;
border-radius: 8px;
font-size: 12px;
font-family: Alibaba PuHuiTi;
font-weight: 400;
color: #ffffff;
}
.bottom-home-sku {
cursor: pointer;
display: flex;
align-items: center;
padding: 2px 5px 2px 0;
background: rgba(255, 255, 255, 0.5);
border-radius: 5px;
margin-right: 6px;
position: relative;
z-index: 1;
}
.bottom-home-sku span,
.top-home-sku span {
font-size: 14px;
vertical-align: top;
margin-left: 5px;
color: #222;
}
.more_wrap :deep(> .delete:hover),
.more_wrap :deep(> .download:hover) {
transform: scale(1.3);
}
.delete,
.download {
display: inline-block;
width: 20px;
height: 20px;
background: rgba(255, 0, 0, 0.678);
padding: 0 4px;
border-radius: 10px;
box-sizing: border-box;
}
.delete svg,
.download svg {
margin-top: -1px;
width: 12px;
height: 12px;
}
.product {
right: 66px;
background: #4168ff;
color: #ffffff;
padding: 0 8px 8px 8px;
border-radius: 18px;
}
.variants {
display: flex;
padding: 5px 0;
}
.variants + .variants {
border-top: 1px solid #ddd;
}
.variants .title {
width: 120px;
min-width: 110px;
color: #303133;
font-size: 14px;
font-weight: bold;
line-height: 20px;
}
.variants li {
display: inline-block;
position: relative;
width: 96px;
padding: 0 6px;
margin-left: 6px;
box-sizing: border-box;
border: 1px solid #dcdfe6;
background: #fff;
border-radius: 4px;
overflow: hidden;
white-space: nowrap;
font-weight: 500;
font-size: 12px;
line-height: 24px;
color: #606266;
cursor: pointer;
text-overflow: ellipsis;
padding-right: 10px;
}
.variants li.active {
border: 1px solid blue;
}
.variants li i {
position: absolute;
top: 7px;
right: 5px;
}
.value {
display: inline-block;
width: 150px;
text-align: left;
color: #000;
font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.wraper:hover .store::after {
display: none;
}
.img_footer_cont {
position: absolute;
display: none;
right: 0;
bottom: 0;
left: 0;
padding-top: 50px;
background-image: linear-gradient(
to top,
rgba(255, 255, 255, 0.9),
rgba(255, 255, 255, 0)
);
}
.img_footer_cont .title {
min-height: 24px;
line-height: 24px;
font-size: 12px;
color: #222;
padding: 5px 8px 0;
}
.wraper:hover .img_footer_cont {
display: block;
}
.top-home-sku {
position: absolute;
bottom: 5px;
left: 5px;
z-index: 888;
display: flex;
align-items: center;
padding: 2px 5px;
background: hsla(0, 0%, 100%, 0.5);
border-radius: 5px;
}
.top-home-sku > span {
width: auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
}
.openly {
top: 5px;
z-index: 3;
right: 5px;
position: absolute;
}
</style>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import cardWrapper from './cardWrapper.vue'
const queryParams = reactive({
name: "",
sku: "",
diy_sku: "",
print_type: "",
category_id: "",
});
const pager = reactive({
page:1,
size:100,
loading: false ,
count: 0,
lists: [] ,
extend: {}
})
const setCurrentImg = (item: any, val: string) => {
item.img_url = val;
};
const categoryTree = ref([])
const resetPage = () => {
}
</script>
<template>
<div class="product">
<el-card>
<el-form
ref="formRef"
class="searchForm"
label-width="80px"
:model="queryParams"
:inline="true"
>
<el-form-item label="商品名称">
<el-input
v-model="queryParams.name"
clearable
maxlength="40"
style="width: 150px"
show-word-limit
placeholder="请输入商品名称"
/>
</el-form-item>
<el-form-item label="SKU">
<el-input
v-model="queryParams.sku"
clearable
style="width: 150px"
placeholder="请输入sku"
/>
</el-form-item>
<el-form-item label="印花类型">
<el-select
v-model="queryParams.print_type"
style="width: 150px"
clearable
placeholder="请选择"
>
<el-option
v-for="(item, index) in ['满印', '局部印']"
:key="index"
:label="item"
:value="index"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="商品分类">
<el-cascader
ref="categoryCascader"
v-model="queryParams.category_id"
:options="categoryTree"
clearable
style="width: 150px"
:props="{
label: 'name',
value: 'id',
emitPath: false,
}"
:show-all-levels="false"
></el-cascader>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetPage">查询</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="product-list">
<el-card
style="flex: 1; flex-shrink: 0; overflow: hidden"
class="!border-none"
shadow="never"
>
<div
v-loading="pager.loading"
class="eltable"
element-loading-text="加载中..."
>
<div v-if="pager.lists.length > 0" id="shopline" class="card-mode">
<div
v-for="(item, index) in pager.lists"
:key="index"
class="card-item"
>
<card-wrapper
:data="item"
:index="index"
is-show-home-sku="bottom"
:is-sku-image-list="true"
:has-width="true"
:is-show-sku="false"
:is-show-sales="false"
:is-custom="true"
@set-current-img="(val) => setCurrentImg(item, val)"
>
<template #operations>
<!-- <img-->
<!-- title="操作日志"-->
<!-- width="24"-->
<!-- height="24"-->
<!-- src="@/assets/images/log.png"-->
<!-- alt=""-->
<!-- @click.stop="viewTheLog(item.id)"-->
<!-- />-->
<img
title="编辑"
width="24"
height="24"
src="@/assets/images/edit.png"
alt=""
@click.stop="editInfo(item, 'edit')"
/>
</template>
<template #price>
<span
style="margin: 0 5px"
class="price"
:title="'建议零售价' + item.sales_price"
>
{{ item.sales_price ? "¥" : ""
}}{{ item.sales_price }}&nbsp;
</span>
</template>
<template #level-time>
<div
class="create-time"
:title="`上架时间:${item.create_time}`"
>
<span>{{ item.create_time }}</span>
</div>
</template>
<template #synthesizeStatus>
<div title="印花状态" style="position: absolute; right: 0">
<el-tag :type="item.print_type == 0 ? '' : 'warning'">
{{ item.print_type == 0 ? "满印" : "局部印" }}
</el-tag>
</div>
</template>
<template #otherContent>
<div class="sds-keyid">
<div class="product-id" @click.stop="copy(item.id)">
<img
:title="'复制' + item.id"
width="20"
src="@/assets/images/id.png"
alt=""
/>
<span :title="item.id">
{{ item.id }}
</span>
</div>
<div class="product-sku" @click.stop="copy(item.sku)">
<span :title="item.sku">
{{ item.sku }}
</span>
</div>
</div>
</template>
</card-wrapper>
</div>
</div>
<div v-else class="empty">暂无数据</div>
</div>
<div class="flex justify-center mt-4">
<div
style="
display: flex;
align-items: center;
margin-left: 20px;
width: max-content;
"
>
<span>一行展示</span>
<el-input
v-model="rowNumber"
type="number"
style="width: 80px; margin: 0 10px"
placeholder="请输入"
clearable
@change="changeRowNumber"
/>
<span>条</span>
</div>
&emsp;&emsp;&emsp;
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
</div>
</div>
</template>
<style scoped lang="scss">
.product{
display: flex;
flex-direction: column;
height: 100%;
}
.searchForm{
::v-deep(.el-form-item){
margin-bottom: 0;
}
}
.justify-center{
display: flex;
justify-content: center;
}
.product-list{
flex: 1;
margin-top: 10px;
flex-shrink: 0;
::v-deep(.el-card){
height: 100%;
.el-card__body{
display: flex;
height: 100%;
flex-direction: column;
.eltable{
flex: 1;
flex-shrink: 0;
overflow: auto;
display: flex;
align-items: center;
justify-content: center;
}
}
}
}
</style>
<template>
<div
class="user-page flex-column card h-100 overflow-hidden"
style="position: relative"
>
<div class="card-header">
<span>系统配置</span>
</div>
<div class="cardBox">
<div v-for="(item, index) in formList" style="width: 600px" :key="index">
<el-form
v-if="item.type === 'RIIN'"
class="form"
ref="formRef"
label-width="120"
:model="item"
>
<div class="formBox">
<el-form-item label="转至RIIN生产">
<el-switch
v-model="item.enable"
class="ml-2"
style="--el-switch-on-color: #42b983"
/>
</el-form-item>
<div class="formContent" v-if="item.enable">
<el-form-item
label="账号"
prop="appKey"
:rules="[{ required: true, message: '请输入RIIN账号' }]"
>
<ElInput
ref="productionOrderRef"
v-model="item.appKey"
placeholder="请输入RIIN账号"
clearable
style="width: 100%"
/></el-form-item>
<el-form-item
label="App Secret"
prop="appSecret"
:rules="[{ required: true, message: '请输入App Secret' }]"
>
<ElInput
ref="productionOrderRef"
v-model="item.appSecret"
placeholder="请输入App Secret"
clearable
style="width: 100%"
/></el-form-item>
</div>
</div>
<ElButton
class="btn"
color="#42b983"
@click="saveConfiguration(item, index)"
>保存配置</ElButton
>
</el-form>
<el-form
v-if="item.type === 'TRACK'"
class="form"
ref="formRef"
label-width="120"
:model="item"
>
<div class="formBox">
<el-form-item label="物流轨迹跟踪">
<el-switch
v-model="item.enable"
class="ml-2"
style="--el-switch-on-color: #42b983"
/>
</el-form-item>
<div class="formContent" v-if="item.enable">
<el-form-item
label="17Track账号"
prop="appKey"
:rules="[{ required: true, message: '请输入17Track账号' }]"
>
<ElInput
ref="productionOrderRef"
v-model="item.appKey"
placeholder="请输入17Track账号"
clearable
style="width: 100%"
/></el-form-item>
<el-form-item
label="账号Token"
prop="token"
:rules="[{ required: true, message: '请输入账号Token' }]"
>
<ElInput
ref="productionOrderRef"
v-model="item.token"
placeholder="请输入账号Token"
clearable
style="width: 100%"
/></el-form-item>
</div>
</div>
<ElButton
class="btn"
color="#42b983"
@click="saveConfiguration(item, index)"
>保存配置</ElButton
>
</el-form>
</div>
</div>
<div class="logBox">
<div>操作日志</div>
<LogList :log-list="logList" style="font-size: 14px; height: 90%" />
</div>
</div>
</template>
<script setup lang="ts">
import {
addExternalAuthorisationApi,
getExternalAuthorisationListApi,
baseExternalAccountLogsApi,
} from '@/api/externalAuth'
import { ExternalAuthListData } from '@/types/api/externalAuth'
interface formType {
id?: number
enable?: boolean
appKey?: string
appSecret?: string
type?: string
}
const formRef = ref()
const logList = ref([])
async function saveConfiguration(item: formType, index: number) {
let loading
try {
await formRef.value[index]?.validate()
const params = { ...item }
const url = !params.id
? 'factory/baseExternalAccount/add'
: 'factory/baseExternalAccount/update'
loading = ElLoading.service({
fullscreen: true,
text: '操作中...',
background: 'rgba(0, 0, 0, 0.3)',
})
const res = await addExternalAuthorisationApi(url, params)
console.log(res)
// ElMessage.success('保存配置成功')
await ElMessageBox.confirm('保存配置成功!!需重新登录才能生效', '提示', {
confirmButtonText: '确定',
showCancelButton: false,
type: 'success',
})
await getDetail()
handleClick()
} catch (error) {
console.log(error)
} finally {
loading?.close()
}
}
const formList = ref<ExternalAuthListData[]>([])
async function getDetail() {
let loading
try {
loading = ElLoading.service({
fullscreen: true,
text: '操作中...',
background: 'rgba(0, 0, 0, 0.3)',
})
const res = await getExternalAuthorisationListApi({}, 1, 100)
const records = res.data.records || []
const newArr = records.filter((el) => el.type)
const typeSet = new Set(newArr.map((el) => el.type))
const hasRIIN = typeSet.has('RIIN')
const hasTRACK = typeSet.has('TRACK')
if (newArr.length === 0) {
formList.value = [{ type: 'RIIN' }, { type: 'TRACK' }]
} else {
const result = [...newArr]
if (!hasRIIN) result.push({ type: 'RIIN' })
if (!hasTRACK) result.push({ type: 'TRACK' })
formList.value = result
}
console.log('Form list:', formList.value)
} catch (error) {
console.error('Failed to get external authorizations:', error)
} finally {
loading?.close()
}
}
async function handleClick() {
try {
let ids = ''
const idList = formList.value.map((el) => el.id).filter((el) => el)
console.log(222, idList)
if (idList.length) {
ids = idList.join(',')
}
console.log(205, ids)
const res = await baseExternalAccountLogsApi({
ids,
})
logList.value = res.data
console.log(res)
} catch (error) {
console.log(error)
}
}
onMounted(async () => {
await getDetail()
handleClick()
})
</script>
<style lang="scss" scoped>
.card-header {
margin-left: 200px;
font-size: 30px;
margin-bottom: 20px;
}
:deep() {
.demo-tabs {
height: calc(100% - 60px);
}
.cardBox {
flex: 3;
overflow-y: scroll;
height: 100%;
.el-card__footer {
border: none !important;
}
.formBox {
width: 100%;
margin-bottom: 15px;
padding: 20px;
border: 1px solid #ebebeb;
border-radius: 8px;
&:last-child {
margin-bottom: 0;
}
.formContent {
padding: 20px;
background-color: #f9f9f9;
border-radius: 5px;
.el-form-item:last-child {
margin-bottom: 0 !important;
}
}
}
.btn {
color: #fff;
}
}
.logBox {
border-top: 1px solid #ebebeb;
padding-top: 10px;
height: 200px;
}
.form {
margin-bottom: 15px;
}
}
</style>
......@@ -22,18 +22,4 @@ export default defineConfig({
resolve: {
alias: { '@': '/src' },
},
server: {
port: 9803,
host: true,
proxy: {
'/api': {
target: 'http://10.168.31.193:8060',
},
'/ws': {
target: 'http://10.168.31.193:8060',
ws: true,
changeOrigin: true,
},
},
},
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment