This commit is contained in:
@@ -197,8 +197,12 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// 默认关闭所有的标签
|
||||
this.handleTagsOption('all')
|
||||
// 默认关闭所有的标签(仅在进入首页时重置)。
|
||||
// 注意:直接输入地址/刷新进入子页面时不能重置,因为 handleTagsOption('all') 会触发
|
||||
// 父组件的 handleCloseTag(res, 'all'),进而 turnToPage('home') 把页面弹回首页,导致深链接失效。
|
||||
if (this.$route.name === this.$config.homeName) {
|
||||
this.handleTagsOption('all')
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.getTagElementByName(this.$route)
|
||||
}, 200)
|
||||
|
||||
@@ -154,6 +154,13 @@ export default {
|
||||
this.addTag({
|
||||
route: this.$store.state.app.homeRoute
|
||||
})
|
||||
// 直接输入地址/刷新进入时,localStorage 恢复的标签栏里没有当前页面,
|
||||
// 这里把它补进标签栏,而不是直接跳回首页,否则深链接会被弹回首页
|
||||
if (this.$route.name && this.$route.name !== this.$config.homeName) {
|
||||
this.addTag({
|
||||
route: this.$route
|
||||
})
|
||||
}
|
||||
this.setBreadCrumb(this.$route)
|
||||
// 如果当前打开页面不在标签栏中,跳到homeName页
|
||||
if (!this.tagNavList.find(item => item.name === this.$route.name)) {
|
||||
|
||||
+35
-2
@@ -24,6 +24,7 @@ const router = new Router({
|
||||
routes,
|
||||
mode: 'history'
|
||||
})
|
||||
|
||||
const LOGIN_PAGE_NAME = 'login'
|
||||
|
||||
const turnTo = (to, access, next) => {
|
||||
@@ -31,6 +32,36 @@ const turnTo = (to, access, next) => {
|
||||
else next({replace: true, name: 'error_401'}) // 无权限,重定向到401页面
|
||||
}
|
||||
|
||||
// 应用首次加载时的地址(此时 vue-router 尚未改写 URL),作为深链接兜底
|
||||
const ENTRY_PATH = window.location.pathname + window.location.search + window.location.hash
|
||||
|
||||
/**
|
||||
* 修正「直接输入地址 / 刷新页面」时深链接失效的问题。
|
||||
*
|
||||
* 动态菜单路由是在首次导航的守卫里才 addRoutes 的,而 vue-router 在进入守卫之前
|
||||
* 就已经用通配符路由(path: '*' → redirect: '/home')把未注册的地址重定向掉了,
|
||||
* 所以守卫里拿到的 to 已经是 /home,原始地址只保留在 to.redirectedFrom 中
|
||||
* (vue-router 3.x 存的是 fullPath 字符串),表现为访问 /delta/gun-code 后停在首页「无反应」。
|
||||
*
|
||||
* 这里在动态路由注册完成后,用新的 matcher 重新匹配原始地址:命中真实路由则再跳一次。
|
||||
* @returns {Boolean} 是否已重新发起跳转(true 时调用方不要再调用 next)
|
||||
*/
|
||||
const turnToRedirectedFrom = (to, next) => {
|
||||
const redirectedFrom = to.redirectedFrom
|
||||
// 兼容字符串(vue-router 3.x 实际类型)与对象两种形态,取不到时回退到入口地址
|
||||
const fromPath = typeof redirectedFrom === 'string'
|
||||
? redirectedFrom
|
||||
: (redirectedFrom && redirectedFrom.path) || ENTRY_PATH
|
||||
// 根路径本身就会重定向到首页;已经停在目标地址也无需再跳
|
||||
if (!fromPath || fromPath === '/' || fromPath === to.fullPath) return false
|
||||
const matched = router.match(fromPath)
|
||||
if (matched.matched.length > 0 && matched.name !== '_no_match') {
|
||||
next({ path: fromPath, replace: true })
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
iView.LoadingBar.start()
|
||||
const token = getToken()
|
||||
@@ -67,7 +98,8 @@ router.beforeEach((to, from, next) => {
|
||||
// 刷新界面菜单
|
||||
store.commit('updateMenuList', list)
|
||||
|
||||
next()
|
||||
// 动态路由已注册,若本次是深链接(被通配符重定向过)则回到原始地址
|
||||
if (!turnToRedirectedFrom(to, next)) next()
|
||||
}).catch(() => {
|
||||
// 获取权限失败,直接放行(保留基础路由),避免卡死
|
||||
next()
|
||||
@@ -75,7 +107,8 @@ router.beforeEach((to, from, next) => {
|
||||
} else {
|
||||
let list = formatMenuList(JSON.parse(menuListStr))
|
||||
store.commit('updateMenuList', list)
|
||||
next()
|
||||
// 动态路由已注册,若本次是深链接(被通配符重定向过)则回到原始地址
|
||||
if (!turnToRedirectedFrom(to, next)) next()
|
||||
}
|
||||
|
||||
}).catch(() => {
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 顶部提示 start -->
|
||||
<div class="entry-tip">
|
||||
<Icon type="md-information-circle" size="16"/>
|
||||
<span>录入后立即生效,公开复制页无需登录即可查看(仅「显示」状态的记录)。</span>
|
||||
<Button type="text" size="small" icon="md-link" @click="copyAddress">复制公开页地址</Button>
|
||||
</div>
|
||||
<!-- 顶部提示 end -->
|
||||
|
||||
<Row :gutter="16">
|
||||
<!-- 录入表单 start -->
|
||||
<Col span="15">
|
||||
<Card :bordered="false" dis-hover>
|
||||
<p slot="title">改枪码录入</p>
|
||||
<Form ref="formItem"
|
||||
:model="formItem"
|
||||
:rules="ruleCustom"
|
||||
:label-width="90"
|
||||
label-position="right"
|
||||
@keydown.native.ctrl.enter="submit('formItem')">
|
||||
<FormItem label="配装名称" prop="name">
|
||||
<Input ref="nameInput" type="text" v-model="formItem.name" maxlength="100" show-word-limit
|
||||
placeholder="必填,如:MP7 冲锋流"/>
|
||||
</FormItem>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<FormItem label="枪械/载具" prop="gunName">
|
||||
<Input type="text" v-model="formItem.gunName" maxlength="100" placeholder="如:MP7"/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="排序" prop="sort">
|
||||
<InputNumber :min="0" v-model="formItem.sort" class="form-input" placeholder="数字越小越靠前"/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
<FormItem label="分类标签" prop="category">
|
||||
<Input type="text" v-model="formItem.category" maxlength="50" placeholder="如:主武器"/>
|
||||
<div class="quick-category">
|
||||
<Tag v-for="item in categoryOptions"
|
||||
:key="item"
|
||||
checkable
|
||||
:checked="formItem.category === item"
|
||||
@on-change="pickCategory(item)">{{ item }}</Tag>
|
||||
</div>
|
||||
</FormItem>
|
||||
<FormItem label="改枪码" prop="code">
|
||||
<Input type="textarea" :rows="8" v-model="formItem.code"
|
||||
placeholder="必填,粘贴游戏内复制的改枪码"/>
|
||||
</FormItem>
|
||||
<FormItem label="备注" prop="remark">
|
||||
<Input type="textarea" :rows="3" maxlength="200" show-word-limit v-model="formItem.remark"
|
||||
placeholder="选填:配装思路 / 适用场景"/>
|
||||
</FormItem>
|
||||
<FormItem label="状态" prop="status">
|
||||
<RadioGroup v-model="formItem.status">
|
||||
<Radio :label="1">
|
||||
<span>显示</span>
|
||||
</Radio>
|
||||
<Radio :label="0">
|
||||
<span>隐藏</span>
|
||||
</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" icon="md-checkmark" :loading="submitLoading" @click="submit('formItem')">
|
||||
保存并继续录入
|
||||
</Button>
|
||||
<Button icon="md-refresh" style="margin-left: 8px" @click="resetForm">重置</Button>
|
||||
<span class="shortcut-tip">快捷键:Ctrl + Enter</span>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Card>
|
||||
</Col>
|
||||
<!-- 录入表单 end -->
|
||||
|
||||
<!-- 最近录入 start -->
|
||||
<Col span="9">
|
||||
<Card :bordered="false" dis-hover>
|
||||
<p slot="title">最近录入</p>
|
||||
<Button slot="extra" type="text" size="small" icon="md-refresh" @click="getRecentList">刷新</Button>
|
||||
<div v-if="recentList.length === 0" class="empty-tip">暂无记录</div>
|
||||
<div v-for="item in recentList" :key="item.id" class="recent-item">
|
||||
<div class="recent-main">
|
||||
<div class="recent-title">
|
||||
<Tag :color="categoryColor(item.category)" size="small">{{ item.category || '未分类' }}</Tag>
|
||||
<span class="recent-name">{{ item.name }}</span>
|
||||
<span v-if="item.gunName" class="recent-gun">{{ item.gunName }}</span>
|
||||
</div>
|
||||
<div class="recent-code">{{ item.code }}</div>
|
||||
<div class="recent-meta">{{ item.createBy || '-' }} · {{ formatDate(item.createDate) }}</div>
|
||||
</div>
|
||||
<Button type="text" size="small" icon="md-copy" @click="copyRow(item)">复制</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
<!-- 最近录入 end -->
|
||||
</Row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getDataList, add } from '@/api/delta/delta-gun-code'
|
||||
import { copyText } from '@/common/js/clipboard'
|
||||
import { formatDate } from '@/common/js/util'
|
||||
|
||||
export default {
|
||||
name: 'delta-gun-code-entry',
|
||||
data () {
|
||||
return {
|
||||
shareUrl: `${window.location.origin}/delta-gun-codes.html`, // 公开复制页面地址
|
||||
categoryOptions: ['主武器', '副武器', '载具配置', '装备'], // 分类快捷选择
|
||||
recentList: [], // 最近录入
|
||||
submitLoading: false,
|
||||
formItem: {
|
||||
id: '',
|
||||
name: '',
|
||||
gunName: '',
|
||||
category: '',
|
||||
code: '',
|
||||
remark: '',
|
||||
sort: 0,
|
||||
status: 1
|
||||
},
|
||||
ruleCustom: {
|
||||
name: [{ required: true, message: '请输入配装名称', trigger: 'blur' }],
|
||||
code: [{ required: true, message: '请输入改枪码', trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatDate (date) {
|
||||
if (!date) return '-'
|
||||
// 后端返回格式不固定(时间戳/ISO/字符串),无法解析时原样展示,避免出现 NaN
|
||||
const time = new Date(date)
|
||||
if (isNaN(time.getTime())) return date
|
||||
return formatDate(date)
|
||||
},
|
||||
submit (formItem) {
|
||||
this.$refs[formItem].validate((valid) => {
|
||||
if (!valid) return
|
||||
this.submitLoading = true
|
||||
add(this.formItem).then(res => {
|
||||
this.submitLoading = false
|
||||
if (res.data.code !== 200) {
|
||||
this.$Message.error(res.data.message || '录入失败')
|
||||
return
|
||||
}
|
||||
this.$Message.success('录入成功!')
|
||||
// 保留分类/排序/状态,方便连续录入同类配装
|
||||
let keep = {
|
||||
category: this.formItem.category,
|
||||
sort: this.formItem.sort,
|
||||
status: this.formItem.status
|
||||
}
|
||||
this.resetForm(keep)
|
||||
this.getRecentList()
|
||||
}).catch(() => {
|
||||
this.submitLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
resetForm (keep) {
|
||||
let temp = keep || {}
|
||||
this.formItem = {
|
||||
id: '',
|
||||
name: '',
|
||||
gunName: '',
|
||||
category: temp.category || '',
|
||||
code: '',
|
||||
remark: '',
|
||||
sort: temp.sort === undefined ? 0 : temp.sort,
|
||||
status: temp.status === undefined ? 1 : temp.status
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
// 仅清除校验状态,不能调用 resetFields(会把保留项一并重置)
|
||||
let form = this.$refs.formItem
|
||||
if (form && form.fields) {
|
||||
form.fields.forEach(field => {
|
||||
field.validateState = ''
|
||||
field.validateMessage = ''
|
||||
})
|
||||
}
|
||||
if (this.$refs.nameInput) {
|
||||
this.$refs.nameInput.focus()
|
||||
}
|
||||
})
|
||||
},
|
||||
pickCategory (item) {
|
||||
this.formItem.category = this.formItem.category === item ? '' : item
|
||||
},
|
||||
getRecentList () {
|
||||
getDataList({ pageNum: 1, pageSize: 5 }).then(res => {
|
||||
let page = res.data.data || {}
|
||||
this.recentList = page.records || []
|
||||
})
|
||||
},
|
||||
copyRow (item) {
|
||||
copyText(item.code).then(ok => {
|
||||
if (ok) {
|
||||
this.$Message.success('改枪码已复制到剪贴板!')
|
||||
} else {
|
||||
this.$Message.error('复制失败,请手动选择复制')
|
||||
}
|
||||
})
|
||||
},
|
||||
copyAddress () {
|
||||
copyText(this.shareUrl).then(ok => {
|
||||
if (ok) {
|
||||
this.$Message.success('公开页面地址已复制!')
|
||||
} else {
|
||||
this.$Message.error('复制失败,请手动选择复制')
|
||||
}
|
||||
})
|
||||
},
|
||||
categoryColor (category) {
|
||||
let map = {
|
||||
'主武器': 'red',
|
||||
'副武器': 'blue',
|
||||
'载具配置': 'green',
|
||||
'装备': 'orange'
|
||||
}
|
||||
return map[category] || 'geekblue'
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.getRecentList()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.entry-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
padding: 8px 12px;
|
||||
background: #f0faff;
|
||||
border: 1px solid #d6eaff;
|
||||
border-radius: 4px;
|
||||
color: #515a6e;
|
||||
font-size: 12px;
|
||||
i {
|
||||
margin-right: 6px;
|
||||
color: #2d8cf0;
|
||||
}
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.quick-category {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.shortcut-tip {
|
||||
margin-left: 12px;
|
||||
color: #c5c8ce;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
padding: 24px 0;
|
||||
text-align: center;
|
||||
color: #c5c8ce;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.recent-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.recent-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.recent-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.recent-name {
|
||||
font-weight: 600;
|
||||
color: #17233d;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.recent-gun {
|
||||
margin-left: 6px;
|
||||
color: #808695;
|
||||
font-size: 12px;
|
||||
}
|
||||
.recent-code {
|
||||
color: #515a6e;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.recent-meta {
|
||||
margin-top: 2px;
|
||||
color: #c5c8ce;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user