502 lines
14 KiB
Vue
502 lines
14 KiB
Vue
<template>
|
||
<div class="public-order-container">
|
||
<el-card class="box-card">
|
||
<div slot="header" class="clearfix">
|
||
<span>订单提交台</span>
|
||
<span class="header-desc">请按照格式提交订单信息</span>
|
||
</div>
|
||
|
||
<el-form :model="form" label-width="80px" label-position="top">
|
||
<el-form-item>
|
||
<template slot="label">
|
||
<span>输入订单信息</span>
|
||
<el-tag type="warning" size="mini" style="margin-left: 10px;">只能提交今天的订单</el-tag>
|
||
</template>
|
||
<el-input
|
||
v-model="form.command"
|
||
type="textarea"
|
||
:rows="12"
|
||
:placeholder="getPlaceholder()"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="submitOrder" :loading="loading" size="medium">
|
||
<i class="el-icon-upload"></i> 提交订单
|
||
</el-button>
|
||
<el-button @click="clearAll" size="medium">
|
||
<i class="el-icon-delete"></i> 清空
|
||
</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<el-divider>响应结果</el-divider>
|
||
|
||
<div v-if="resultList.length === 0" style="padding: 12px 0;">
|
||
<el-empty description="暂无响应" />
|
||
</div>
|
||
<div v-else>
|
||
<div v-for="(msg, idx) in resultList" :key="idx" class="msg-block">
|
||
<div class="msg-header">
|
||
<span>第 {{ idx + 1 }} 段</span>
|
||
<el-button size="mini" type="success" @click="copyOne(msg)">复制此段</el-button>
|
||
</div>
|
||
<el-input :value="msg" type="textarea" :rows="8" readonly />
|
||
</div>
|
||
<div style="margin-top: 8px;">
|
||
<el-button size="mini" type="primary" @click="copyAll">复制全部</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 使用说明 -->
|
||
<el-divider>使用说明</el-divider>
|
||
<div class="usage-guide">
|
||
<el-collapse>
|
||
<el-collapse-item title="订单格式说明" name="1">
|
||
<div class="guide-content">
|
||
<p><strong>请严格按照以下格式填写订单信息:</strong></p>
|
||
<pre class="format-example">单:
|
||
{{ getTodayDate() }} 001
|
||
备注:测试订单
|
||
分销标记:H-TF
|
||
型号:ZQD180F-EB200
|
||
链接:https://item.jd.com/...
|
||
下单付款:1650
|
||
后返金额:50
|
||
地址:张三13800138000上海市浦东新区张江高科技园区...
|
||
物流链接:https://...
|
||
订单号:1234567890
|
||
下单人:张三</pre>
|
||
<p class="tips"><i class="el-icon-warning"></i> 重要提示:订单日期必须是今天({{ getTodayDate() }}),每个字段都不能省略</p>
|
||
</div>
|
||
</el-collapse-item>
|
||
<el-collapse-item title="注意事项" name="2">
|
||
<div class="guide-content">
|
||
<ul>
|
||
<li><strong style="color: #E6A23C;">只能提交今天的订单,历史订单不允许提交</strong></li>
|
||
<li>请确保订单信息准确无误</li>
|
||
<li>每次只能提交一个订单</li>
|
||
<li>提交成功后会显示确认信息</li>
|
||
<li>如遇错误,请检查格式和日期是否正确</li>
|
||
<li>限流策略:每半小时最多提交120个订单</li>
|
||
</ul>
|
||
</div>
|
||
</el-collapse-item>
|
||
</el-collapse>
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- 地址重复验证码弹窗 -->
|
||
<el-dialog
|
||
title="地址重复验证"
|
||
:visible.sync="verifyDialogVisible"
|
||
width="400px"
|
||
:close-on-click-modal="false"
|
||
:close-on-press-escape="false"
|
||
>
|
||
<div style="text-align: center;">
|
||
<el-alert
|
||
:title="verifyMessage"
|
||
type="warning"
|
||
:closable="false"
|
||
style="margin-bottom: 20px;"
|
||
/>
|
||
<div style="font-size: 24px; font-weight: bold; color: #409EFF; margin: 20px 0;">
|
||
{{ verifyCode }}
|
||
</div>
|
||
<el-input
|
||
v-model="verifyInput"
|
||
placeholder="请输入上方四位数字验证码"
|
||
maxlength="4"
|
||
style="width: 200px;"
|
||
@keyup.enter.native="handleVerify"
|
||
/>
|
||
</div>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="verifyDialogVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="handleVerify" :loading="verifyLoading">确认</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 页尾导航 -->
|
||
<PublicFooterNav />
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { submitPublicOrder, submitPublicOrderWithForce } from '@/api/public/order'
|
||
import PublicFooterNav from '@/components/PublicFooterNav'
|
||
|
||
export default {
|
||
name: 'PublicOrderSubmit',
|
||
components: {
|
||
PublicFooterNav
|
||
},
|
||
data() {
|
||
return {
|
||
form: { command: '' },
|
||
loading: false,
|
||
resultList: [],
|
||
// 验证码相关
|
||
verifyDialogVisible: false,
|
||
verifyCode: '',
|
||
verifyInput: '',
|
||
verifyMessage: '',
|
||
verifyLoading: false,
|
||
pendingCommand: '' // 待执行的命令(验证通过后执行)
|
||
}
|
||
},
|
||
methods: {
|
||
getPlaceholder() {
|
||
const today = new Date().toISOString().split('T')[0]
|
||
return `请按照以下格式输入订单信息(注意:订单日期必须是今天 ${today}):
|
||
单:
|
||
${today} 001
|
||
备注:测试订单
|
||
分销标记:H-TF
|
||
型号:ZQD180F-EB200
|
||
链接:https://...
|
||
下单付款:1650
|
||
后返金额:50
|
||
地址:张三13800138000上海市浦东新区...
|
||
物流链接:https://...
|
||
订单号:1234567890
|
||
下单人:张三`
|
||
},
|
||
copyOne(text) {
|
||
if (!text) return
|
||
this.doCopy(text)
|
||
},
|
||
copyAll() {
|
||
if (!this.resultList || this.resultList.length === 0) return
|
||
const text = this.resultList.join('\n\n')
|
||
this.doCopy(text)
|
||
},
|
||
doCopy(text) {
|
||
if (navigator.clipboard) {
|
||
navigator.clipboard.writeText(text).then(() => {
|
||
this.$message.success('复制成功')
|
||
}).catch(() => {
|
||
this.fallbackCopyText(text)
|
||
})
|
||
} else {
|
||
this.fallbackCopyText(text)
|
||
}
|
||
},
|
||
fallbackCopyText(text) {
|
||
const ta = document.createElement('textarea')
|
||
ta.value = text
|
||
document.body.appendChild(ta)
|
||
ta.focus()
|
||
ta.select()
|
||
try {
|
||
document.execCommand('copy')
|
||
this.$message.success('复制成功')
|
||
} catch (e) {
|
||
this.$message.error('复制失败')
|
||
}
|
||
document.body.removeChild(ta)
|
||
},
|
||
submitOrder() {
|
||
const cmd = (this.form.command || '').trim()
|
||
if (!cmd) {
|
||
this.$message.error('请输入订单信息')
|
||
return
|
||
}
|
||
|
||
// 检查是否以"单:"开头
|
||
if (!cmd.startsWith('单:') && !cmd.startsWith('单:')) {
|
||
this.$message.error('订单信息必须以"单:"开头')
|
||
return
|
||
}
|
||
|
||
this.loading = true
|
||
submitPublicOrder({ command: cmd }).then(res => {
|
||
this.loading = false
|
||
if (res && (res.code === 200 || res.msg === '操作成功')) {
|
||
const data = res.data
|
||
if (Array.isArray(data)) {
|
||
this.resultList = data
|
||
} else if (typeof data === 'string') {
|
||
this.resultList = data ? [data] : []
|
||
} else {
|
||
this.resultList = []
|
||
}
|
||
|
||
// 检查是否是地址重复错误
|
||
if (this.checkAddressDuplicate(this.resultList)) {
|
||
// 显示验证码弹窗
|
||
this.showVerifyDialog(cmd)
|
||
return
|
||
}
|
||
|
||
// 检查是否有警告信息
|
||
this.checkWarningAlert(this.resultList)
|
||
|
||
// 如果没有警告,显示成功提示
|
||
if (!this.hasWarning(this.resultList)) {
|
||
this.$message.success('订单提交成功')
|
||
}
|
||
} else {
|
||
this.$message.error(res && res.msg ? res.msg : '提交失败')
|
||
this.resultList = []
|
||
}
|
||
}).catch(error => {
|
||
this.loading = false
|
||
const errorMsg = error.response?.data?.msg || error.message || '提交失败,请稍后重试'
|
||
this.$message.error(errorMsg)
|
||
this.resultList = []
|
||
})
|
||
},
|
||
// 检查是否是地址重复错误
|
||
checkAddressDuplicate(resultList) {
|
||
if (!resultList || resultList.length === 0) return false
|
||
for (let i = 0; i < resultList.length; i++) {
|
||
const result = resultList[i]
|
||
if (typeof result === 'string' && result.startsWith('ERROR_CODE:ADDRESS_DUPLICATE')) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
},
|
||
// 显示验证码弹窗
|
||
showVerifyDialog(command) {
|
||
// 生成四位随机数字验证码
|
||
this.verifyCode = String(Math.floor(1000 + Math.random() * 9000))
|
||
this.verifyInput = ''
|
||
this.verifyMessage = '检测到地址重复,请输入验证码以强制生成表单'
|
||
this.pendingCommand = command
|
||
this.verifyDialogVisible = true
|
||
},
|
||
// 处理验证码验证
|
||
handleVerify() {
|
||
if (!this.verifyInput || this.verifyInput.length !== 4) {
|
||
this.$message.error('请输入四位数字验证码')
|
||
return
|
||
}
|
||
if (this.verifyInput !== this.verifyCode) {
|
||
this.$message.error('验证码错误,请重新输入')
|
||
this.verifyInput = ''
|
||
return
|
||
}
|
||
|
||
// 验证通过,使用forceGenerate参数重新提交
|
||
this.verifyLoading = true
|
||
submitPublicOrderWithForce({ command: this.pendingCommand }).then(res => {
|
||
this.verifyLoading = false
|
||
this.verifyDialogVisible = false
|
||
if (res && (res.code === 200 || res.msg === '操作成功')) {
|
||
const data = res.data
|
||
if (Array.isArray(data)) {
|
||
this.resultList = data
|
||
} else if (typeof data === 'string') {
|
||
this.resultList = data ? [data] : []
|
||
} else {
|
||
this.resultList = []
|
||
}
|
||
|
||
// 检查是否有警告信息
|
||
this.checkWarningAlert(this.resultList)
|
||
|
||
// 如果没有警告,显示成功提示
|
||
if (!this.hasWarning(this.resultList)) {
|
||
this.$message.success('订单提交成功(已强制生成)')
|
||
}
|
||
} else {
|
||
this.$message.error(res && res.msg ? res.msg : '提交失败')
|
||
this.resultList = []
|
||
}
|
||
}).catch(error => {
|
||
this.verifyLoading = false
|
||
const errorMsg = error.response?.data?.msg || error.message || '提交失败,请稍后重试'
|
||
this.$message.error(errorMsg)
|
||
this.resultList = []
|
||
})
|
||
},
|
||
clearAll() {
|
||
this.form.command = ''
|
||
this.resultList = []
|
||
},
|
||
checkWarningAlert(resultList) {
|
||
if (!resultList || resultList.length === 0) return
|
||
|
||
// 检查是否有以[炸弹]开头的警告消息
|
||
const warningMessages = resultList
|
||
.filter(msg => {
|
||
return msg && typeof msg === 'string' && msg.trim().includes('[炸弹]')
|
||
})
|
||
.map(msg => {
|
||
// 移除所有的[炸弹]标记
|
||
return msg.trim().replace(/\[炸弹\]\s*/g, '').trim()
|
||
})
|
||
|
||
if (warningMessages.length > 0) {
|
||
// 显示警告弹窗
|
||
this.$alert(warningMessages.join('\n\n'), '⚠️ 警告提示', {
|
||
confirmButtonText: '我已知晓',
|
||
type: 'warning',
|
||
center: true,
|
||
customClass: 'warning-alert-dialog',
|
||
showClose: false,
|
||
closeOnClickModal: false,
|
||
closeOnPressEscape: false,
|
||
dangerouslyUseHTMLString: false
|
||
}).catch(() => {})
|
||
}
|
||
},
|
||
hasWarning(resultList) {
|
||
if (!resultList || resultList.length === 0) return false
|
||
return resultList.some(msg => msg && typeof msg === 'string' && msg.trim().includes('[炸弹]'))
|
||
},
|
||
getTodayDate() {
|
||
return new Date().toISOString().split('T')[0]
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.public-order-container {
|
||
min-height: 100vh;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
padding: 20px;
|
||
padding-bottom: calc(80px + 20px); /* 为页尾导航留出空间 */
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.box-card {
|
||
max-width: 1000px;
|
||
width: 100%;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
.clearfix {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.clearfix span:first-child {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
}
|
||
|
||
.header-desc {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
font-weight: normal;
|
||
}
|
||
|
||
.msg-block {
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.msg-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin: 6px 0;
|
||
font-weight: 500;
|
||
color: #606266;
|
||
}
|
||
|
||
.usage-guide {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.guide-content {
|
||
padding: 10px;
|
||
}
|
||
|
||
.guide-content p {
|
||
margin: 10px 0;
|
||
line-height: 1.8;
|
||
color: #606266;
|
||
}
|
||
|
||
.format-example {
|
||
background-color: #f5f7fa;
|
||
padding: 15px;
|
||
border-radius: 4px;
|
||
border-left: 3px solid #409eff;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
overflow-x: auto;
|
||
margin: 15px 0;
|
||
}
|
||
|
||
.tips {
|
||
color: #e6a23c;
|
||
font-size: 13px;
|
||
padding: 10px;
|
||
background-color: #fdf6ec;
|
||
border-radius: 4px;
|
||
border: 1px solid #f5dab1;
|
||
}
|
||
|
||
.guide-content ul {
|
||
list-style: none;
|
||
padding: 0;
|
||
}
|
||
|
||
.guide-content ul li {
|
||
padding: 8px 0;
|
||
color: #606266;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.guide-content ul li:before {
|
||
content: "•";
|
||
color: #409eff;
|
||
font-weight: bold;
|
||
display: inline-block;
|
||
width: 1em;
|
||
margin-left: 10px;
|
||
}
|
||
</style>
|
||
|
||
<style>
|
||
/* 全局样式:警告弹窗 */
|
||
.warning-alert-dialog {
|
||
width: 80vw !important;
|
||
max-width: 800px !important;
|
||
min-width: 400px !important;
|
||
}
|
||
|
||
.warning-alert-dialog .el-message-box__header {
|
||
padding: 20px 20px 15px !important;
|
||
}
|
||
|
||
.warning-alert-dialog .el-message-box__title {
|
||
font-size: 18px !important;
|
||
font-weight: 700 !important;
|
||
}
|
||
|
||
.warning-alert-dialog .el-message-box__message {
|
||
font-size: 15px !important;
|
||
font-weight: 600 !important;
|
||
color: #e6a23c !important;
|
||
white-space: pre-wrap !important;
|
||
word-break: break-word !important;
|
||
line-height: 1.8 !important;
|
||
max-height: 60vh !important;
|
||
overflow-y: auto !important;
|
||
padding: 15px 20px !important;
|
||
}
|
||
|
||
.warning-alert-dialog .el-message-box__btns {
|
||
text-align: center !important;
|
||
padding: 15px 20px 20px !important;
|
||
}
|
||
|
||
.warning-alert-dialog .el-message-box__btns .el-button {
|
||
padding: 12px 40px !important;
|
||
font-size: 14px !important;
|
||
font-weight: 600 !important;
|
||
}
|
||
</style>
|
||
|