提交 52986b37 authored 作者: 龙菲's avatar 龙菲

增加预览3D文物的弹窗

上级 b86916cc
......@@ -2,7 +2,7 @@
## Project setup
```
npm install
cnpm install 或者npm install 推荐使用cnpm 因为会存在vue-video-player版本问题
```
### Compiles and hot-reloads for development
......
module.exports = {
presets: [
// https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app
'@vue/cli-plugin-babel/preset'
'@vue/cli-plugin-babel/preset',
],
'env': {
'development': {
......
......@@ -15,7 +15,7 @@
},
"dependencies": {
"axios": "0.18.1",
"core-js": "3.6.5",
"core-js": "^3.32.2",
"echarts": "^5.4.0",
"el-tree-transfer": "^2.4.7",
"element-ui": "^2.15.6",
......@@ -23,6 +23,7 @@
"js-md5": "^0.7.3",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"online-3d-viewer": "^0.10.0",
"path-to-regexp": "2.4.0",
"vue": "2.6.10",
"vue-awesome-swiper": "^3.1.3",
......
......@@ -17,8 +17,14 @@
>
</el-popconfirm>
</span>
<span class="view-3d" v-else-if="op.type == 'view3D'" >
<svg-icon icon-class="three"></svg-icon>{{ op.title }}</span
<span
class="view-3d"
v-else-if="op.type == 'view3D'"
@click="clickOperation(op)"
>
<el-link type="primary">
<svg-icon icon-class="three"></svg-icon>{{ op.title }}</el-link
></span
>
<el-link
size="mini"
......
<template>
<!-- online_3d_viewer -->
<div
class="online_3d_viewer"
model="/static/3d/yshl.FBX"
:backgroundcolor="backgroundcolor"
></div>
</template>
<script>
import * as ThreeEngine from "online-3d-viewer";
export default {
props: {
// 预览的3D文件的地址
url: {
type: String,
default: "",
},
// 模型背景颜色
backgroundcolor: {
type: String,
default: "0,0,0,80%",
},
},
mounted() {
this.init();
},
methods: {
// 初始化3D查看器
init() {
// tell the engine where to find the libs folder
ThreeEngine.SetExternalLibLocation("libs");
// init all viewers on the page
ThreeEngine.Init3DViewerElements();
},
},
};
</script>
<style lang="scss" scoped>
.online_3d_viewer {
width: 100%;
height: 100%;
min-height: 300px;
min-width: 300px;
}
</style>
......@@ -8,9 +8,9 @@ import getPageTitle from '@/utils/get-page-title'
import { getMenu } from "./api/user";
import { addRouter } from "./utils/addRouter";
import user from "./store/modules/user";
// NProgress.configure({
// showSpinner: false
// }) // NProgress Configuration
NProgress.configure({
showSpinner: false
}) // NProgress Configuration
const whiteList = ['/login'] // no redirect whitelist
......@@ -58,16 +58,13 @@ router.afterEach((to, from) => {
function gotoRouter(to, next) {
getMenu() // 获取动态路由的方法
.then(res => {
console.log(res.data.menus);
// console.log(res.data.menus);
const asyncRouter = addRouter(res.data.menus); // 进行递归解析
return asyncRouter;
//TODO:前端暂时写死
// const asyncRouter = addRouter(); // 进行递归解析
// return asyncRouter;
// store.dispatch("user/setroles", res.data.permit);
})
.then(asyncRouter => {
// 后置添加404页面,防止刷新404
......
......@@ -141,11 +141,9 @@ const actions = {
menus
} = res.data
let menuObj = getMenuObj(menus)
function getMenuObj(menu) {
let obj = {}
traverseMenuList(menu || []);
function traverseMenuList(arr) {
if (arr.length > 0) {
arr.map((item) => {
......
......@@ -62,6 +62,7 @@
</template>
<script>
import { dialogMixins } from "./dialogMixins";
export default {
name: "PreviewDialog",
components: {},
......@@ -90,9 +91,6 @@ export default {
data() {
return {};
},
async created() {
// console.log(this.videos);
},
methods: {
// 取消编辑
cancelForm() {
......
<template>
<el-dialog
:visible="visible"
width="70%"
style="height: 50vh"
:before-close="handleClose"
top="5vh"
lock-scroll
>
<Viewer3d />
</el-dialog>
</template>
<script>
import Viewer3d from "@/components/Viewer3d";
export default {
components: {
Viewer3d,
},
data() {
return {
visible: false,
};
},
methods: {
handleClose() {
this.visible = false;
},
},
};
</script>
<style></style>
......@@ -140,6 +140,12 @@
@handleClose="handleImportRecordClose"
/>
<View3dDialog
ref="View3dDialog"
:visible="view3dDialogVisible"
@handleClose="handleView3dDialogClose"
/>
<el-image-viewer
v-if="imgViewerVisible"
:on-close="closeImgViewer"
......@@ -162,7 +168,7 @@ import {
import InfoEditDialog from "./components/InfoEditDialog";
import UploadListDialog from "./components/UploadListDialog";
import ImportRecordDialog from "./components/ImportRecordDialog";
import View3dDialog from "./components/View3dDialog";
import { importZip } from "@/utils/file";
import { getToken } from "@/utils/auth";
export default {
......@@ -173,6 +179,7 @@ export default {
SearchBar,
ImportRecordDialog,
UploadListDialog,
View3dDialog,
"el-image-viewer": () =>
import("element-ui/packages/image/src/image-viewer"),
},
......@@ -255,6 +262,7 @@ export default {
filesList: [], //上传当中的文件队列
uploadCount: 0, //处于上传中的文件数量,当等于fileList的时候就关闭弹窗,请求完毕一个就++
cancelUploadArr: [], //保存每个文件上传接口对应的取消请求的函数[fn,fn,fn...],点叉叉关闭弹窗就遍历这个全部执行一遍就取消了所以请求
view3dDialogVisible: false, //3D文件在线浏览弹窗可见性
};
},
......@@ -337,11 +345,14 @@ export default {
}
},
async handleOperation(value, row) {
console.log(value, row);
switch (value.type) {
case "add":
this.editVisible = true;
break;
case "view":
case "view3D":
console.log(value, row);
this.$refs.View3dDialog.visible = true;
break;
case "edit":
let detailRes = await getRCDetailById({ crId: row.crId });
......@@ -361,7 +372,6 @@ export default {
// debugger
// this.multiUploadVisible = true;
// console.log("this.multiUploadVisible", this.multiUploadVisible);
break;
case "downloadTemplate":
......@@ -536,10 +546,14 @@ export default {
num: 1,
};
},
// 关闭导入记录弹窗
handleImportRecordClose() {
this.importRecordVisible = false;
},
// 关闭预览3D弹窗
handleView3dDialogClose() {
this.view3dDialogVisible = false;
},
},
};
</script>
......
<template>
<el-dialog
:visible.sync="dialogVisible"
width="80%"
:before-close="handleClose"
top="5vh"
lock-scroll
>
<div class="title" slot="title">
<div class="divider"></div>
<div class="label">{{ title }}</div>
</div>
<div class="dialog-content">
<el-tabs v-model="activeName" v-loading="dialogLoading">
<!-- <el-tab-pane label="基本信息" name="info">
<div class="container">
<BaseInfo :displayDetail="displayDetail" :dicts="dicts" />
</div>
</el-tab-pane> -->
<el-tab-pane label="效果预览" name="page" v-if="!isBoutique">
<div
class="container"
v-if="dicts && Object.keys(displayDetail).length > 0"
>
<NavBar />
<NormalStyle
v-if="displayDetail.themeType == '1'"
:displayDetail="displayDetail"
:dicts="dicts"
/>
<ChStyle
v-if="displayDetail.themeType == '2'"
:displayDetail="displayDetail"
:dicts="dicts"
/>
<RedStyle
v-if="displayDetail.themeType == '3'"
:displayDetail="displayDetail"
:dicts="dicts"
/>
<Footer />
</div>
</el-tab-pane>
<el-tab-pane label="审批历史记录" name="history">
<div class="container">
<el-steps :active="displayDetail.historyChecks.length">
<el-step
v-for="(item, index) in displayDetail.historyChecks"
:title="getStepTitle(item)"
:key="index"
:icon="getStepIcon(item)"
>
<div slot="description">
<div class="name">{{ item.createName }}</div>
<div class="time">{{ item.createTime }}</div>
<div class="desc">{{ item.remark }}</div>
</div>
</el-step>
</el-steps>
</div>
</el-tab-pane>
</el-tabs>
<div class="dialog-footer">
<span v-if="displayDetail.checkStatus == 0">
<el-button
size="mini"
@click.native="handleCancel"
style="margin-right: 6px"
>取消</el-button
>
<el-popover
placement="top-end"
width="400"
trigger="manual"
v-model="popoverVisible"
>
<div>
<el-form
size="mini"
:model="dialogForm"
prop="remark"
:rules="rules"
ref="form"
>
<el-form-item label="驳回意见" prop="remark">
<el-input
type="textarea"
placeholder="驳回意见"
v-model="dialogForm.remark"
size="mini"
:rows="4"
>
</el-input>
<el-button
size="mini"
type="primary"
style="float: right; margin-top: 16px"
@click.native="handleCheck(-2)"
>确定</el-button
>
</el-form-item>
</el-form>
</div>
<el-button
size="mini"
slot="reference"
type="danger"
icon="el-icon-close"
@click.native="handleShowPopover"
>驳回</el-button
>
</el-popover>
<el-button
size="mini"
style="margin-left: 6px"
type="primary"
icon="el-icon-check"
@click.native="handleCheck(1)"
>同意</el-button
>
</span>
<span v-else>
<el-button size="mini" type="primary" @click.native="handleClose">关闭</el-button>
</span>
</div>
</div>
</el-dialog>
</template>
<script>
import NormalStyle from "@/views/display/components/templates/NormalStyle.vue";
// import NormalStyle from "./components/NormalStyle.vue";
import ChStyle from "@/views/display/components/templates/ChStyle.vue";
import RedStyle from "@/views/display/components/templates/RedStyle.vue";
import BaseInfo from "./BaseInfo";
import { mapGetters } from "vuex";
import { postCheck } from "@/api/display";
import NavBar from "@/components/NavBar";
import Footer from "@/components/Footer";
import { titles } from "@/utils/boutiqueTitles";
export default {
name: "PreviewDialog",
components: {
NormalStyle,
ChStyle,
RedStyle,
BaseInfo,
NavBar,
Footer,
},
props: {
visible: {
type: Boolean,
default: false,
},
displayDetail: {
type: Object,
default: () => ({}),
},
loading: {
type: Boolean,
default: false,
},
},
computed: {
...mapGetters(["dicts"]),
title() {
return "审批——" + this.displayDetail.title;
},
getStepTitle(item) {
return (item) => {
switch (item.checkStatus) {
case 0:
return "发起审核";
case 1:
return "审核通过";
case -2:
return "驳回";
}
};
},
getStepIcon(item) {
return (item) => {
switch (item.checkStatus) {
case 0:
return "el-icon-s-promotion";
case 1:
return "el-icon-s-claim";
case -2:
return "el-icon-circle-close";
}
};
},
isBoutique() {
this.activeName = titles.includes(this.displayDetail.title)
? "history"
: "page";
return titles.includes(this.displayDetail.title);
},
},
watch: {
visible: {
handler: function (value) {
this.dialogVisible = value;
},
immediate: true,
deep: true,
},
loading(value) {
this.dialogLoading = value;
},
},
data() {
return {
dialogVisible: false,
dialogForm: {
remark: "", //驳回意见
},
activeName: "page",
historyChecks: [],
dialogLoading: false,
rules: {
remark: [
{
required: true,
message: "请输入驳回意见",
trigger: "blur",
},
],
},
popoverVisible: false,
};
},
async mounted() {
await this.$store.dispatch("dict/getDictList", [
"displayType",
"displayCharacter",
]);
},
methods: {
handleClose(done) {
this.$emit("handleClose");
this.activeName = titles.includes(this.displayDetail.title)
? "history"
: "page";
},
async handleCheck(checkStatus) {
if (checkStatus == -2) {
this.$refs.form.validate(async (valid) => {
if (valid) {
this.dialogLoading = true;
const params = {
sourceId: this.displayDetail.exhibitionId,
checkStatus,
remark: this.dialogForm.remark,
sourceType: "biz_exhibition",
sourceName: this.displayDetail.title,
};
let res = await postCheck(params);
if (res.code == 0) {
this.$message.success("操作成功!");
this.handleClose();
this.$emit("refresh");
this.$refs.form.resetFields();
}
this.dialogLoading = false;
this.popoverVisible = false;
}
});
} else {
this.dialogLoading = true;
const params = {
sourceId: this.displayDetail.exhibitionId,
checkStatus,
remark: this.dialogForm.remark,
sourceType: "biz_exhibition",
sourceName: this.displayDetail.title,
};
let res = await postCheck(params);
if (res.code == 0) {
this.$message.success("操作成功!");
this.handleClose();
this.$emit("refresh");
this.$refs.form.resetFields();
}
this.dialogLoading = false;
this.popoverVisible = false;
}
},
handleShowPopover() {
this.popoverVisible = true;
},
handleCancel() {
this.handleClose();
this.$emit("refresh");
this.$refs.form.resetFields();
this.popoverVisible = false;
this.dialogLoading = false;
},
},
};
</script>
<style lang="scss" scoped>
::v-deep .el-step.is-horizontal {
max-width: 300px !important;
}
.title {
display: flex;
margin-bottom: 16px;
.divider {
width: 8px;
border-left: 4px solid #409eff;
margin-right: 8px;
}
.label {
font-weight: bold;
}
}
.dialog-content {
padding: 0 32px;
.container {
border: 1px dashed #409eff;
height: calc(64vh);
overflow: auto;
padding: 46px;
.desc {
color: #666 !important;
}
.name {
color: #333 !important;
}
.time {
color: #999 !important;
}
}
}
::v-deep .el-dialog__wrapper {
overflow: hidden !important;
}
::v-deep .el-step__title {
color: #333;
font-weight: bold;
}
// ::v-deep .el-dialog__body {
// position: relative;
// }
.dialog-footer {
display: flex;
justify-content: flex-end;
margin-top: 40px;
// position: absolute;
// width:100%;
bottom: 0;
position: sticky;
// height: 100px;
}
// ::v-deep .el-dialog__footer {
// position: sticky !important;
// }
.video-container {
background-color: #000;
display: flex;
justify-content: center;
}
.el-dialog__body {
padding: 0 20px 30px 20px;
}
</style>
export const displayTabletitle = [{
prop: "title",
label: "标题",
columnAlign: 'center',
showOverFlowToolTip: true,
},
// {
// prop: "keyword",
// label: "关键词",
// columnAlign: 'center',
// showOverFlowToolTip: true,
// },
{
prop: "deptName",
label: "展览单位",
columnAlign: 'center',
showOverFlowToolTip: true,
},
{
prop: "type",
label: "展览类型",
columnAlign: 'center',
isDisplayType: true,
showOverFlowToolTip: true,
},
{
prop: "faceImageUrl",
label: "封面",
columnAlign: 'center',
isFaceImage: true,
width: 130
},
{
prop: "checkStatus",
label: "状态",
columnAlign: 'center',
},
{
prop: "checkRemark",
label: "备注",
columnAlign: 'center',
showOverFlowToolTip: true,
},
]
export const operates = {
operate: true,
label: "操作",
width: "260px",
minwidth: "220px",
titleAlign: "center",
columnAlign: "center",
}
export const approvalOperations = [{
type: 'approval',
title: '审批'
}
]
export const viewOperations = [{
type: 'view',
title: '查看详情'
}
]
export const crTabletitle = [{
prop: "name",
label: "名称",
columnAlign: 'center',
showOverFlowToolTip: true,
},
// {
// prop: "level",
// label: "文物级别",
// columnAlign: 'center',
// },
// {
// prop: "detailSize",
// label: "尺寸",
// columnAlign: 'center',
// },
// {
// prop: "textureType",
// label: "质地",
// columnAlign: 'center',
// },
// {
// prop: "type",
// label: "类别",
// width: 100,
// columnAlign: 'center',
// isCulturalRelicType:true
// },
// {
// prop: "createId",
// label: "创建人",
// columnAlign: 'center',
// },
// {
// prop: "createTime",
// label: "创建时间",
// columnAlign: 'center',
// },
{
prop: "deptName",
label: "馆藏单位",
columnAlign: 'center',
showOverFlowToolTip: true,
},
{
prop: "regionName",
label: "所属地",
columnAlign: 'center',
showOverFlowToolTip: true,
},
// {
// prop: "intro",
// label: "馆藏介绍",
// columnAlign: 'center',
// showOverFlowToolTip: true,
// width: 120,
// },
{
prop: "themeWord",
label: "主题词",
columnAlign: 'center',
showOverFlowToolTip: true,
},
{
prop: "faceImageUrl",
label: "封面",
columnAlign: 'center',
isFaceImage: true,
},
{
prop: "status",
label: "上下架状态",
width: 100,
columnAlign: 'center',
isStatus: true
},
{
prop: "num",
label: "数量",
columnAlign: 'center',
},
{
prop: "sourceWay",
label: "来源方式",
columnAlign: 'center',
showOverFlowToolTip: true,
},
{
prop: "remark",
label: "备注",
columnAlign: 'center',
},
// directory 文件夹
// flag3d 是否有3d图片
// updateId 更新人
]
export const importOperates = {
operate: true,
label: "操作",
width: "220px",
minwidth: "220px",
titleAlign: "center",
columnAlign: "center",
}
export const importOperations = [{
type: 'delete',
title: '删除记录及文物'
},]
\ No newline at end of file
<template>
<div class="app-container">
<div class="top-bar">
<SearchBar :config="searchConfig" @search="search" @reset="reset" />
<!-- <el-button
type="primary"
@click.native="handleOperation({ type: 'add' })"
icon="el-icon-plus"
>
发布</el-button
>
</div> -->
</div>
<el-tabs v-model="activeName" type="border-card" ref="tabs">
<el-tab-pane :label="item.label" :name="item.name" v-for="(item, tabIndex) in tabs" :key="tabIndex">
<TablePage :data="dataList[tabIndex].records" :tableTitle="tableTitle" :operates="tableOperates" v-loading="loading">
<template v-slot:checkStatus="data">
<!-- {{ data.scope.checkStatus }} -->
<el-tag type="primary" v-if="data.scope.checkStatus == 0">
待审核
</el-tag>
<el-tag type="success" v-if="data.scope.checkStatus == 1">
已通过
</el-tag>
<el-tag type="danger" v-if="data.scope.checkStatus == -2">
已驳回
</el-tag>
</template>
<template v-slot:displayType="data">
{{ dicts.displayType[data.scope.type] }}
</template>
<template v-slot:faceImageUrl="data">
<img :src="
$getFullUrl(data.scope.faceImagePressUrl || data.scope.faceImageUrl)
" alt="暂无图片" v-if="$getFullUrl(data.scope.faceImagePressUrl || data.scope.faceImageUrl)"
style="cursor: pointer" width="100px" @click="handelPreviewImages(data.scope.faceImageUrl)" />
</template>
<template v-slot:operates="scope">
<TableOperation :operations="tableOperations" :rawData="scope.scope.row" @handleOperation="handleOperation">
</TableOperation>
</template>
</TablePage>
<el-pagination style="margin: 16px 0" @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page="Number(dataList[currentTabIndex].current)" :page-sizes="[10, 20, 50, 100]"
:page-size="Number(dataList[currentTabIndex].size)" layout="total, sizes, prev, pager, next, jumper"
:total="Number(dataList[currentTabIndex].total)" class="pagination">
</el-pagination>
</el-tab-pane>
</el-tabs>
<PreviewDisplayDialog v-if="Object.keys(curPreviewObj).length > 0"
:visible="previewDialogVisible"
:displayDetail="curPreviewObj"
:loading="previewLoading"
@handleClose="handleClosePreviewDialog"
@refresh="loadData" />
<el-image-viewer v-if="imgViewerVisible" :on-close="closeImgViewer" :url-list="imgList" />
</div>
</template>
<script>
import TablePage from "@/components/Table/TablePage.vue";
import TableOperation from "@/components/Table/TableOperation.vue";
import PreviewDisplayDialog from './components/PreviewDisplayDialog.vue'
import { displayTabletitle, operates, approvalOperations, viewOperations } from "./config";
import {
getListPer,
getDisplayCheckById,
} from "@/api/display";
import SearchBar from "@/components/SearchBar";
import { mapGetters } from "vuex";
export default {
components: {
TablePage,
TableOperation,
SearchBar,
PreviewDisplayDialog,
"el-image-viewer": () =>
import("element-ui/packages/image/src/image-viewer"),
},
data() {
let that = this;
return {
dataList: [
{
record: [],
size: 10,
current: 1,
total: 0,
},
{
record: [],
size: 10,
current: 1,
total: 0,
},
{
record: [],
size: 10,
current: 1,
total: 0,
}
],
searchConfig: [
{
prop: "title",
type: "input",
label: "展览名称",
},
],
loading: false,
imgViewerVisible: false,
previewDialogVisible: false,
curPreviewObj: {}, //当前预览的对象
currentPageIds: [], //当前的id数组,用于给详情页切换用
tabs: [{
name: '0',
label: '待审核'
}, {
name: '1',
label: '已通过'
},
{
name: '-2',
label: '已驳回'
}
],
activeName: '0',
currentTabIndex: 0,
dialogLoading:false
};
},
computed: {
...mapGetters(["dicts"]),
tableTitle() {
return displayTabletitle;
},
tableOperates() {
return operates;
},
tableOperations() {
if (this.activeName == '0') {
return approvalOperations
} else {
return viewOperations
}
}
},
watch: {
activeName(value) {
this.currentTabIndex = Number(Math.abs(value)) //当前索引正好对应状态的绝对值
this.loadData()
}
},
async created() {
await this.$store.dispatch("dict/getDictList", ["displayType"]);
this.loadData();
},
methods: {
async search(form) {
var params = {
page: 1,
limit: this.dataList[this.currentTabIndex].size,
...form,
};
if (params.status == "") {
delete params.status;
}
let res = await getListPer(params);
if (res.code == 0) {
this.$set(this.dataList, this.currentTabIndex, res.data)
// this.dataList[this.currentTabIndex] = res.data
// this.currentPageIds = this.dataList[this.currentTabIndex].records.map((item) => {
// return item.exhibitionId;
// });
}
},
reset() {
this.loadData();
},
// 加载表格数据
async loadData() {
this.loading = true
var params = {
page: this.dataList[this.currentTabIndex].current,
limit: this.dataList[this.currentTabIndex].size,
checkStatus: Number(this.activeName)
};
let res = await getListPer(params);
if (res.code == 0) {
this.$set(this.dataList, this.currentTabIndex, res.data)
// this.currentPageIds = this.dataList[this.currentTabIndex].records.map((item) => {
// return item.exhibitionId;
// });
}
this.loading = false
},
async handleOperation(value, row) {
console.log(value, row);
// debugger
if (value.type == 'approval' || value.type == 'view') {
this.previewLoading = true
if (row) {
this.previewDialogVisible = true;
let res = await getDisplayCheckById({ exhibitionId: row.exhibitionId });
this.curPreviewObj = res.data;
this.previewLoading = false;
}
}
},
// 多选
handleSelectionChange(val) {
this.multipleSelection = val;
},
// 改变页容量
handleSizeChange(value) {
this.dataList[this.currentTabIndex].size = value;
this.loadData();
},
// 改变当前显示页
handleCurrentChange(value) {
this.dataList[this.currentTabIndex].current = value;
this.loadData();
},
// 关闭Dialog
handleClose() {
this.editDialogVisible = false;
this.form = {
status: 1,
faceImage: "", // 封面(图片1张)
images: "", //展览图片
videos: "", //展览视频
audios: "", //展览音频
exhibitionUnits: [], //布展单元
};
},
// 关闭预览图片
closeImgViewer() {
this.imgViewerVisible = false;
},
// 预览图片
handelPreviewImages(images) {
this.imgViewerVisible = true;
this.imgList = [images];
},
handleClosePreviewDialog() {
this.previewDialogVisible = false;
}
},
};
</script>
<style lang="scss" scoped>
.top-bar {
display: flex;
justify-content: space-between;
flex-direction: column;
height: 68px;
}
.tools {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
.tools-item {
display: flex;
&:last-child {
justify-content: flex-end;
}
}
}
</style>
\ No newline at end of file
差异被折叠。
......@@ -181,8 +181,6 @@ export default {
this.$store
.dispatch("user/login", params)
.then(async () => {
// debugger
// await this.$store.dispatch('user/getMenuList')
this.$router.push({ path: this.redirect || "/" });
this.loading = false;
})
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论