提交 0f42b6ca authored 作者: longfei's avatar longfei

登录注册规则校验、mock接口联调登录登出

上级 4bffbcf8
NODE_ENV = 'development' NODE_ENV = 'development'
VUE_APP_CURRENTMODE = 'dev' VUE_APP_CURRENTMODE = 'dev'
VUE_APP_BASEURL = '本地开发api地址' VUE_APP_BASE_API = 'http://127.0.0.1:4523/m1/1100718-0-default/'
\ No newline at end of file \ No newline at end of file
差异被折叠。
...@@ -10,8 +10,11 @@ ...@@ -10,8 +10,11 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"axios": "^0.27.2",
"core-js": "^3.8.3", "core-js": "^3.8.3",
"element-ui": "^2.15.9", "element-ui": "^2.15.9",
"js-cookie": "^3.0.1",
"mockjs": "^1.1.0",
"qrcodejs2": "^0.0.2", "qrcodejs2": "^0.0.2",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-router": "^3.5.1", "vue-router": "^3.5.1",
......
import request from '@/utils/request'
export function login(data) {
return request({
url: '/user/login',
method: 'post',
data
})
}
export function updatePassword(data) {
return request({
url: '/auth/user/modifyPass',
method: 'put',
data
})
}
export function getInfo(token) {
return request({
url: '/user/getUserInfo',
method: 'get',
params: {
token
}
})
}
export function logout() {
return request({
url: '/qljc-web-vue/user/logout',
method: 'post'
})
}
// 手机短信验证
export function sendSmsCode(params) {
return request({
url: '/auth/api/smsCaptcha',
method: 'post',
params: params
})
}
/** *
* 获取eventType常量
*/
export function getEventTypePage() {
return request({
url: '/eventType/page?size=20',
method: 'get'
})
}
/** *
* 获取用户姓名
*/
export function getUserNameById(params) {
return request({
url: '/auth/user/userInfo',
method: 'get',
params
})
}
// /** *
// * 上传头像
// */
// export function uploadFileByFile(data) {
// return request({
// url: '/common/oss/uploadFileByFile',
// method: 'post',
// data,
// headers: {
// 'Content-Type': 'multipart/form-data'
// }
// })
// }
// /** *
// * 更新头像
// */
// export function updateHeadPic(params) {
// return request({
// url: '/auth/user/updateHeadPic',
// method: 'get',
// params
// })
// }
<template> <template>
<div class="nav"> <div class="nav">
<div class="title">贵州省精品展览展示平台</div> <div class="title">贵州省精品展览展示平台</div>
<div class="tabs" v-for="(item, index) in pages" :key="index"> <div class="tabs" v-for="(item, index) in pages" :key="index">
<router-link :to="item.path">{{ item.name }}</router-link> <router-link :to="item.path">{{ item.name }}</router-link>
</div> </div>
<div class="operation"> <div class="operation">
<span class="operation-item">登录</span>| <span v-if="isLogged">欢迎你,{{ userInfo.userName }}</span>
<router-link to="/personal" class="operation-item">个人中心</router-link>| <router-link v-if="!isLogged" to="/login" class="operation-item"
<span class="operation-item">退出</span> >登陆</router-link
</div> >|
<router-link to="/personal" class="operation-item">个人中心</router-link>|
<span class="logout">
<el-popover
placement="bottom"
width="200"
trigger="click"
v-model="logoutDialogVisible"
>
<p>确定退出吗?</p>
<div style="text-align: right; margin: 0">
<el-button
size="mini"
type="text"
@click="logoutDialogVisible = false"
>取消</el-button
>
<el-button type="primary" size="mini" @click="handleLogOut"
>确定</el-button
>
</div>
<span class="operation-item" slot="reference">退出</span>
</el-popover>
</span>
</div>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters } from "vuex";
export default { export default {
name: "NavBar", name: "NavBar",
computed: {
...mapGetters(["token", "userInfo"]),
isLogged() {
return this.userInfo && this.userInfo.userName;
},
},
data() { data() {
return { return {
pages: [ pages: [
...@@ -39,34 +70,42 @@ export default { ...@@ -39,34 +70,42 @@ export default {
path: "/culturalRelic", path: "/culturalRelic",
}, },
], ],
userName: "",
logoutDialogVisible: false,
}; };
}, },
methods: {
async handleLogOut() {
await this.$store.dispatch("user/logout");
this.logoutDialogVisible = false;
this.$router.push(`/login?redirect=${this.$route.fullPath}`);
},
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
a{ a {
color: #000; color: #000;
text-decoration: none; text-decoration: none;
} }
.nav{ .nav {
display: flex; display: flex;
height: 120px; height: 120px;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
// padding: 0 16%;
margin: auto; margin: auto;
width: 1200px; width: 1200px;
.title{ .title {
font-size: 30px; font-size: 30px;
font-weight: bold; font-weight: bold;
} }
.tabs{ .tabs {
display: flex; display: flex;
margin-right: 5px; margin-right: 5px;
} }
.operation{ .operation {
.operation-item{ .operation-item {
margin-right: 8px; margin-right: 8px;
cursor: pointer; cursor: pointer;
color: #000; color: #000;
......
import Vue from 'vue' import Vue from 'vue'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import store from './store' import store from './store/index'
import ElementUI from 'element-ui'; import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css'; import 'element-ui/lib/theme-chalk/index.css';
......
...@@ -53,6 +53,19 @@ const routes = [ ...@@ -53,6 +53,19 @@ const routes = [
name: 'personal', name: 'personal',
component: () => import('@/views/personal') component: () => import('@/views/personal')
}, },
// 登录
{
path: '/login',
name: 'login',
component: () => import('@/views/login')
},
// 注册页
{
path: '/register',
name: 'register',
component: () => import('@/views/register')
},
] ]
const router = new VueRouter({ const router = new VueRouter({
......
const getters = {
// 用户
token: state => state.user.token,
avatar: state => state.user.userInfo.avatar,
name: state => state.user.userInfo.name,
userInfo: state => state.user.userInfo,
}
export default getters
\ No newline at end of file
import Vue from 'vue' import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
import getters from './getters'
import user from './modules/user'
Vue.use(Vuex) Vue.use(Vuex)
export default new Vuex.Store({ const store = new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: { modules: {
} user,
},
getters
}) })
export default store
import {
login,
// getInfo,
// getAsyncRouter
} from '@/api/user'
import {
getToken,
setToken,
removeToken,
getUserInfo,
setUserInfo,
removeUserInfo,
} from '@/utils/auth'
const getDefaultState = () => {
return {
token: getToken(),
userInfo: getUserInfo(),
}
}
const state = getDefaultState()
const mutations = {
RESET_STATE: (state) => {
Object.assign(state, getDefaultState())
},
SET_TOKEN: (state, token) => {
state.token = token
console.log('登录后存在vuex中token',token);
},
SET_INFO: (state, userInfo) => {
state.userInfo = userInfo
},
}
const actions = {
// user login
login({
commit
}, userInfo) {
return new Promise((resolve, reject) => {
login(userInfo).then(response => {
if (response.code != 0) {
userInfo.fail(response)
} else {
const {
data,
token
} = response
commit('SET_TOKEN', token)
commit('SET_INFO', data)
setToken(token)
setUserInfo(data)
resolve()
}
}).catch(error => {
reject(error)
})
})
},
// get user info
getInfo({
commit,
state
}) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const {
data
} = response
if (!data) {
reject('Verification failed, please Login again.')
}
commit('SET_INFO', data)
setUserInfo(data)
resolve(data)
}).catch(error => {
reject(error)
})
})
},
// user logout
logout({
commit,
state
}) {
return new Promise((resolve, reject) => {
// logout(state.token).then(() => {
removeToken() // must remove token first
removeUserInfo()
commit('RESET_STATE')
resolve()
})
},
// remove token
resetToken({
commit
}) {
return new Promise(resolve => {
removeToken() // must remove token first
removeUserInfo()
commit('RESET_STATE')
resolve()
})
},
}
export default {
namespaced: true,
state,
mutations,
actions
}
import Cookies from 'js-cookie'
const TokenKey = 'bwgzl_token'
const UserInfoKey = 'bwgzl_user_info'
export function getToken() {
console.log('刷新后getToken',Cookies.get(TokenKey));
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token, { sameSite: 'None', secure: true })
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
export function getUserInfo() {
console.log('刷新后getUserInfo',JSON.parse(localStorage.getItem(UserInfoKey)));
return JSON.parse(localStorage.getItem(UserInfoKey))
}
export function setUserInfo(userInfo) {
return localStorage.setItem(UserInfoKey, JSON.stringify(userInfo))
}
export function removeUserInfo() {
return localStorage.removeItem(UserInfoKey)
}
/**** request.js ****/
// 导入axios // 导入axios
import axios from 'axios' import axios from 'axios'
// 使用element-ui Message做消息提醒 // 使用element-ui Message做消息提醒
import { Message} from 'element-ui'; import {
Message
} from 'element-ui';
import {
getToken
} from '@/utils/auth'
//1. 创建新的axios实例, //1. 创建新的axios实例,
const service = axios.create({ const service = axios.create({
// 公共接口--这里注意后面会讲 // baseURL: process.env.BASE_API,
baseURL: process.env.BASE_API, baseURL: '/api',
// 超时时间 单位是ms,这里设置了3s的超时时间 // 超时时间 单位是ms,这里设置了3s的超时时间
timeout: 3 * 1000 timeout: 3 * 1000
}) })
// 2.请求拦截器 // 2.请求拦截器
service.interceptors.request.use(config => { service.interceptors.request.use(config => {
//发请求前做的一些处理,数据转化,配置请求头,设置token,设置loading等,根据需求去添加 //发请求前做的一些处理,数据转化,配置请求头,设置token,设置loading等,根据需求去添加
config.data = JSON.stringify(config.data); //数据转化,也可以使用qs转换 config.data = JSON.stringify(config.data); //数据转化,也可以使用qs转换
config.headers = { config.headers = {
'Content-Type':'application/x-www-form-urlencoded' //配置请求头 'Content-Type': 'application/x-www-form-urlencoded' //配置请求头
} }
//注意使用token的时候需要引入cookie方法或者用本地localStorage等方法,推荐js-cookie const token = getToken();
// const token = getCookie('名称');//这里取token之前,你肯定需要先拿到token,存一下 if (token) {
// if(token){ config.params = {
// config.params = {'token':token} //如果要求携带在参数中 'token': token
// config.headers.token= token; //如果要求携带在请求头中 } //如果要求携带在参数中
// } config.headers.token = token; //如果要求携带在请求头中
}
return config return config
}, error => { }, error => {
Promise.reject(error) Promise.reject(error)
...@@ -30,33 +35,32 @@ service.interceptors.request.use(config => { ...@@ -30,33 +35,32 @@ service.interceptors.request.use(config => {
// 3.响应拦截器 // 3.响应拦截器
service.interceptors.response.use( service.interceptors.response.use(
(response) => { (response) => {
const res = response.data const res = response.data
if (response.config.responseType !== 'blob') { if (response.config.responseType !== 'blob') {
if (res.code !== 0) { if (res.code !== 0) {
Message({ Message({
message: res.msg || 'Error', message: res.msg || 'Error',
type: 'error', type: 'error',
duration: 3 * 1000, duration: 3 * 1000,
}) })
return Promise.reject(new Error(res.msg || 'Error')) return Promise.reject(new Error(res.msg || 'Error'))
} else { } else {
return res
}
}
return res return res
}, }
(error) => {
let res = error.response
if (error.response) {
if (res.status == 401) {
Vue.prototype.parentFns.portal_logout()
}
}
return Promise.reject(error)
} }
return res
},
(error) => {
let res = error.response
if (error.response) {
if (res.status == 401) {
Vue.prototype.parentFns.portal_logout()
}
}
return Promise.reject(error)
}
) )
//4.导入文件 //4.导入文件
export default service export default service
<!-- -->
<template>
<div class="login">
<el-tabs v-model="activeName" class="login-box">
<el-tab-pane label="账号登录" name="account">
<el-form
:model="accountForm"
:label-position="labelPosition"
:rules="accountRules"
ref="accountForm"
>
<el-form-item
label="账号"
:label-width="formLabelWidth"
prop="account"
>
<el-input
v-model="accountForm.account"
autocomplete="off"
clearable
></el-input>
</el-form-item>
<el-form-item label="密码" :label-width="formLabelWidth" prop="pwd">
<el-input
v-model="accountForm.pwd"
autocomplete="off"
type="password"
clearable
show-password
></el-input>
<div class="register">
<router-link to="/register" @click.native="closeDialog">
没有账号?去注册
</router-link>
</div>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="手机号登录" name="phoneNumber">
<el-form
:model="phoneForm"
label-position="labelPosition"
:rules="phoneRules"
ref="phoneForm"
>
<el-form-item
label="手机号"
:label-width="formLabelWidth"
prop="phoneNumber"
>
<el-input
v-model="phoneForm.phoneNumber"
autocomplete="off"
clearable
></el-input>
</el-form-item>
<el-form-item
label="验证码"
:label-width="formLabelWidth"
prop="captcha"
>
<el-input
v-model="phoneForm.captcha"
autocomplete="off"
style="width: 46%; margin-right: 8px"
clearable
></el-input>
<el-button :disabled="isDisabled" @click.native="handleSend">{{
sendButtonText
}}</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
<el-button @click="handleSubmitLogin">登 陆</el-button>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
data() {
return {
accountForm: {
account: "",
pwd: "",
},
phoneForm: {
phoneNumber: "",
captcha: "",
},
accountRules: {
account: [{ message: "请输入账号", required: true }],
pwd: [{ message: "请输入密码", required: true }],
},
phoneRules: {
phoneNumber: [
{ required: true, message: "请输入手机号", trigger: "blur" },
{
pattern: /^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/,
message: "请输入合法手机号/电话号",
trigger: "blur",
},
],
captcha: [{ message: "请输入验证码", required: true }],
},
formLabelWidth: "70px",
labelPosition: "right",
activeName: "account",
isDisabled: true,
sendButtonText: "发送验证码",
second: 60,
};
},
watch: {
"phoneForm.phoneNumber": {
handler: function (value) {
this.$refs["phoneForm"].validateField("phoneNumber", (errorMessage) => {
let valid = errorMessage == "";
if (valid) {
this.isDisabled = false;
} else {
this.isDisabled = true;
}
});
},
deep: true,
},
},
mounted() {},
methods: {
handleClickLogin() {
this.loginDialogVisible = true;
},
// 登录提交
handleSubmitLogin() {
if (this.activeName == "account") {
this.$refs["accountForm"].validate((isTrue) => {
if (isTrue) {
if (this.accountForm.account) {
var params = {
userName: this.accountForm.account,
pwd: this.accountForm.pwd,
};
this.$store
.dispatch("user/login", params)
.then(() => {
this.$message.success("登录成功!");
var redirect = this.$route.query.redirect;
this.$router.push({
path: redirect || "/",
});
})
.catch((err) => {
this.$message.error(err);
console.log(err);
});
}
}
});
} else {
this.$refs["phoneForm"].validate((isTrue) => {
if (isTrue) {
// TODO:调用登录接口
this.$message.info("需调用登录接口");
this.closeDialog();
}
});
}
},
// 点击发送验证码
handleSend() {
this.isDisabled = true;
this.second = 60;
var timer = setInterval(() => {
if (this.second < 1) {
this.isDisabled = false;
clearInterval(timer);
timer = null;
this.sendButtonText = "发送验证码";
} else {
this.second--;
this.sendButtonText = `${this.second}秒后重新发送`;
}
}, 1 * 1000);
},
},
};
</script>
<style lang="scss" scoped>
.login {
width: 500px;
margin: 50px auto;
.login-box {
width: 500px;
margin: auto;
}
}
.register {
display: flex;
justify-content: flex-end;
}
.dialog-footer {
display: flex;
justify-content: center;
}
</style>
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
<el-button @click="editPassword">修改密码</el-button> <el-button @click="editPassword">修改密码</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-dialog title="修改个人信息" :visible.sync="dialogFormVisible" > <el-dialog title="修改个人信息" :visible.sync="dialogFormVisible" width="400px">
<el-form :model="infoData" v-if="isEditBasicInfo" :label-position="'right'"> <el-form :model="infoData" v-if="isEditBasicInfo" label-position="right" label-width="80px">
<el-form-item label="昵称" > <el-form-item label="昵称" >
<el-input v-model="infoData.name" autocomplete="off"></el-input> <el-input v-model="infoData.name" autocomplete="off"></el-input>
</el-form-item> </el-form-item>
...@@ -21,25 +21,22 @@ ...@@ -21,25 +21,22 @@
<el-input v-model="infoData.phone" autocomplete="off"></el-input> <el-input v-model="infoData.phone" autocomplete="off"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-form :model="pwdData" v-else> <el-form :model="pwdData" v-else label-position="right" label-width="80px">
<el-form-item label="旧密码"> <el-form-item label="旧密码" >
<el-input <el-input
v-model="pwdData.oldPwd" v-model="pwdData.oldPwd"
autocomplete="off"
type="password" type="password"
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="新密码" > <el-form-item label="新密码" >
<el-input <el-input
v-model="pwdData.newPwd" v-model="pwdData.newPwd"
autocomplete="off"
type="password" type="password"
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="重复密码" > <el-form-item label="重复密码" >
<el-input <el-input
v-model="pwdData.repeatPwd" v-model="pwdData.repeatPwd"
autocomplete="off"
type="password" type="password"
></el-input> ></el-input>
</el-form-item> </el-form-item>
...@@ -73,6 +70,7 @@ export default { ...@@ -73,6 +70,7 @@ export default {
isEditBasicInfo: true, isEditBasicInfo: true,
}; };
}, },
methods: { methods: {
editInfo() { editInfo() {
this.dialogFormVisible = true; this.dialogFormVisible = true;
...@@ -84,4 +82,9 @@ export default { ...@@ -84,4 +82,9 @@ export default {
}, },
}, },
}; };
</script> </script>
\ No newline at end of file <style lang="scss" scoped>
.el-input{
width: 80%;
}
</style>
\ No newline at end of file
<template>
<div class="register">
<el-form
:model="form"
status-icon
:rules="rules"
ref="registerForm"
label-width="100px"
class="register-form"
>
<el-form-item label="手机号" prop="phoneNumber">
<el-input v-model="form.phoneNumber" autocomplete="off"></el-input>
<!-- <el-button style="margin-left:16px">发送验证码</el-button> -->
</el-form-item>
<el-form-item label="验证码" prop="captcha">
<el-input
v-model="form.captcha"
autocomplete="off"
style="width: 46%; margin-right: 8px"
clearable
></el-input>
<el-button :disabled="isDisabled" @click.native="handleSend">{{
sendButtonText
}}</el-button>
</el-form-item>
<el-form-item label="账号" prop="account">
<el-input v-model="form.account" autocomplete="off"></el-input>
</el-form-item>
<!-- <el-form-item label="手机号" prop="pass">
<el-input v-model="ruleForm.account" autocomplete="off"></el-input>
<el-button>发送验证码</el-button>
</el-form-item> -->
<el-form-item label="密码" prop="pass">
<el-input
type="password"
v-model="form.pass"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="checkPass">
<el-input
type="password"
v-model="form.checkPass"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('registerForm')"
>提交</el-button
>
<el-button @click="resetForm('registerForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
var validatePass = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入密码"));
} else {
var pattern =
/^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\\W_!@#$%^&*`~()-+=]+$)(?![a-z0-9]+$)(?![a-z\\W_!@#$%^&*`~()-+=]+$)(?![0-9\\W_!@#$%^&*`~()-+=]+$)[a-zA-Z0-9\\W_!@#$%^&*`~()-+=]{8,30}$/;
if (!pattern.test(value)) {
callback(
new Error(
"密码长度至少8位,至少含数字,大写字母,小写字母,特殊符其中三种"
)
);
}
if (this.form.checkPass !== "") {
this.$refs.registerForm.validateField("checkPass");
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === "") {
callback(new Error("请再次输入密码"));
} else if (value !== this.form.pass) {
callback(new Error("两次输入密码不一致!"));
} else {
callback();
}
};
return {
form: {
account: "",
pass: "",
checkPass: "",
captcha: "",
},
isDisabled: true,
sendButtonText: "发送验证码",
second: 60,
rules: {
account: [
{ message: "请输入账号", trigger: "blur", required: true },
{
pattern: /^[0-9a-zA-Z|_]{6,20}$/g,
message: "账号长度6-20个字符,只能包括字母、数字、下划线",
trigger: "blur",
},
],
phoneNumber: [
{ required: true, message: "请输入手机号", trigger: "blur" },
{
pattern: /^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/,
message: "请输入合法手机号/电话号",
trigger: "blur",
},
],
captcha: [{ message: "请输入验证码", required: true }],
pass: [{ validator: validatePass, trigger: "blur", required: true }],
checkPass: [
{ validator: validatePass2, trigger: "blur", required: true },
],
},
};
},
watch: {
"form.phoneNumber": {
handler: function (value) {
this.$refs["registerForm"].validateField("phoneNumber", (errorMessage) => {
let valid = errorMessage == "";
if (valid) {
this.isDisabled = false;
} else {
this.isDisabled = true;
}
});
},
deep: true,
},
},
methods: {
// 点击发送验证码
handleSend() {
this.isDisabled = true;
this.second = 60;
var timer = setInterval(() => {
if (this.second < 1) {
this.isDisabled = false;
clearInterval(timer);
timer = null;
this.sendButtonText = "发送验证码";
} else {
this.second--;
this.sendButtonText = `${this.second}秒后重新发送`;
}
}, 1 * 1000);
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
alert("submit!");
} else {
console.log("error submit!!");
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
},
};
</script>
<style lang="scss" scoped>
.register {
width: 1200px;
margin: 50px auto;
.register-form {
width: 500px;
margin: auto;
}
}
</style>
\ No newline at end of file
...@@ -13,7 +13,16 @@ module.exports = defineConfig({ ...@@ -13,7 +13,16 @@ module.exports = defineConfig({
host: 'localhost', host: 'localhost',
port: '8080', port: '8080',
open: true, open: true,
hot:true hot: true,
proxy: {
'/api': {
target: 'http://127.0.0.1:4523/m1/1100718-0-default',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}, },
publicPath: './', publicPath: './',
chainWebpack: config => { chainWebpack: config => {
...@@ -37,5 +46,5 @@ module.exports = defineConfig({ ...@@ -37,5 +46,5 @@ module.exports = defineConfig({
} }
return configuration return configuration
}, },
// TODO:接口代理
}) })
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论