新增请求配置,对接登录接口

This commit is contained in:
gyq 2024-02-26 11:46:21 +08:00
parent 5298ff2569
commit 9744a968bb
13 changed files with 457 additions and 261 deletions

View File

@ -1,10 +1,10 @@
<template>
<el-config-provider size="large">
<div class="container">
<div class="left" v-if="route.path !== 'login'">
<div class="left" v-if="route.path != '/login'">
<left-menu />
</div>
<div class="view">
<div :class="{ view: route.path != '/login' }">
<router-view />
<!-- <div class="wrapper">
<div class="animation">
@ -21,10 +21,9 @@
</template>
<script setup>
import leftMenu from '@/components/leftMenu.vue'
import { useRouter, useRoute } from "vue-router";
import { ref } from "vue";
import { useRouter, useRoute } from "vue-router"
import leftMenu from '@/components/leftMenu.vue'
const route = useRoute()

14
src/api/product.js Normal file
View File

@ -0,0 +1,14 @@
import request from "@/utils/request.js";
/**
* 查询分类信息
* @param {*} params
* @returns
*/
export function queryCategory(params) {
return request({
method: "get",
url: "product/queryCategory",
params
});
}

9
src/api/user.js Normal file
View File

@ -0,0 +1,9 @@
import request from "@/utils/request.js";
export function login(data) {
return request({
method: "post",
url: "login/login",
data,
});
}

View File

@ -0,0 +1,13 @@
<!-- 取餐号组件 -->
<template>
<el-dialog title="修改取餐号" v-model="dialogVisible">
<el-input v-model="number" placeholder="请输入取餐号"></el-input>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue';
const dialogVisible = ref(false)
const number = ref('')
</script>

View File

@ -1,5 +1,5 @@
import { createRouter, createWebHashHistory } from "vue-router";
import home from "@/views/home.vue";
import { createRouter, createWebHashHistory } from "vue-router"
import home from "@/views/home/index.vue"
const routes = [
{

View File

@ -1,13 +1,23 @@
import { defineStore } from "pinia";
export const useUserStore = defineStore({
import { defineStore } from "pinia"
import { login } from '@/api/user'
import useStorage from '@/utils/useStorage'
export const useUser = defineStore({
id: "user",
state: () => ({
name: "张三",
token: "sas121sasdADSAD",
userInfo: useStorage.get('userInfo'),
token: useStorage.get('token'),
}),
actions: {
login(data) {
this.name = data;
},
},
});
// 登录
userlogin(param) {
return login(param).then(res => {
this.userInfo = res
this.token = res.token
useStorage.set("token", this.token);
useStorage.set("userInfo", this.userInfo);
return this.userInfo;
})
}
}
})

View File

@ -0,0 +1,11 @@
/**
* 生成范围随机数
* @param {Object} Min
* @param {Object} Max
*/
export function RandomNumBoth(Max, Min = 0) {
var Range = Max - Min;
var Rand = Math.random();
var num = Min + Math.round(Rand * Range); //四舍五入
return num;
}

View File

@ -1,8 +1,68 @@
import axios from "axios";
import { ElMessage } from "element-plus";
import useStorage from '@/utils/useStorage'
import router from '@/router'
const request = axios.create({
baseURL: "http://localhost",
timeout: 100000,
const service = axios.create({
baseURL: import.meta.env.MODE == 'development' ? '/api/' : '/api/admin',
// withCredentials: true, // 跨域请求时发送 cookies
timeout: 5000, // 请求超时
});
request.interceptors.request.use((config) => {});
// 请求拦截器
service.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么 token
if (useStorage.get("token")) {
// 让每个请求携带 token
// ['X-Token'] 是自定义标题键
// 请根据实际情况修改
config.headers["token"] = useStorage.get("token");
config.headers["loginName"] = useStorage.get("userInfo").loginName;
config.headers["clientType"] = 'pc';
// config.headers['Content-Type'] = 'application/json'
}
return config;
},
(error) => {
// 处理请求错误
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
(response) => {
// 对响应数据做点什么
if (+response.status === 200) {
if (+response.data.code == '0') {
return response.data.data;
} else if (+response.data.code == '9999') {
ElMessage.error('登录已过期,请重新登录')
useStorage.clear()
router.replace("/login")
window.location.reload()
return Promise.reject('登录已过期,请重新登录')
} else {
// 响应错误
ElMessage.error(response.data.msg)
return Promise.reject(response.data.msg)
}
}
},
(error) => {
// 对响应错误做点什么
if (error.message.indexOf("timeout") != -1) {
ElMessage.error("网络超时");
} else if (error.message == "Network Error") {
ElMessage.error("网络连接错误");
} else {
console.log(error);
if (error.response.data) ElMessage.error(error.response.statusText);
else ElMessage.error("接口路径找不到");
}
return Promise.reject(error);
}
);
export default service;

15
src/utils/useStorage.js Normal file
View File

@ -0,0 +1,15 @@
export default {
get(key) {
return JSON.parse(localStorage.getItem(key))
},
set(key, value) {
localStorage.setItem(key, JSON.stringify(value))
},
del(key) {
localStorage.removeItem(key)
},
clear() {
localStorage.clear()
}
}

View File

@ -0,0 +1,221 @@
<!-- 商品列表 -->
<template>
<div class="header">
<div class="menus">
<div class="item" :class="{ active: categorysActive == index }" v-for="(item, index) in categorys"
:key="item.id" @click="categorysActive = index">
<el-text>{{ item.name }}</el-text>
</div>
</div>
<div class="all">
<el-icon class="icon">
<Menu />
</el-icon>
</div>
</div>
<div class="search_wrap">
<div class="input">
<el-input placeholder="商品名称或首字母简称" prefix-icon="Search"></el-input>
</div>
<el-button :icon="shopListType == 'text' ? 'PictureRounded' : 'PriceTag'" @click="changeShopListType"></el-button>
</div>
<div class="shop_list" :class="{ img: shopListType == 'img' }">
<div class="item_wrap" v-for="item in 7" :key="item">
<div class="item">
<div class="dot">2</div>
<div class="cover" v-if="shopListType == 'img'">
<el-image
src="https://img1.baidu.com/it/u=2183780444,2807225961&fm=253&fmt=auto&app=138&f=JPEG?w=600&h=400"
style="width: 100%;height: 100%;" fit="cover"></el-image>
</div>
<div class="name"><el-text>酱香拿铁</el-text></div>
<div class="empty" v-if="shopListType == 'text'"></div>
<div class="price">
<el-text>0.03</el-text>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { queryCategory } from '@/api/product'
import { ref, onMounted } from 'vue'
import { useUser } from "@/store/user.js"
const store = useUser();
const shopListType = ref('text')
const categorys = ref([])
const categorysActive = ref(0)
//
function changeShopListType() {
if (shopListType.value == 'text') {
shopListType.value = 'img'
} else {
shopListType.value = 'text'
}
}
//
async function queryCategoryAjax() {
try {
const res = await queryCategory({
shopId: store.userInfo.shopId,
page: 1,
pageSize: 100
})
categorys.value = res.list
} catch (error) {
console.log(error)
}
}
onMounted(() => {
queryCategoryAjax()
})
</script>
<style scoped lang="scss">
.header {
display: flex;
height: 80px;
justify-content: space-between;
border-bottom: 1px solid #ececec;
}
.menus {
display: flex;
padding: 0 10px;
.item {
padding: 0 10px;
display: flex;
align-items: center;
position: relative;
span {
font-size: 24px;
}
&.active {
&::after {
content: "";
width: 70%;
height: 2px;
border-radius: 4px;
position: absolute;
bottom: 0;
left: 15%;
background-color: var(--primary-color);
}
span {
color: #333;
}
}
}
}
.all {
width: 80px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
&::before {
content: "";
height: 60%;
border-left: 1px solid #ececec;
position: absolute;
left: 0;
top: 20%;
}
.icon {
color: #555;
font-size: 20px;
}
}
.search_wrap {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 20px;
}
.shop_list {
max-height: calc(100vh - 40px - 80px - 80px);
overflow-y: auto;
display: flex;
flex-wrap: wrap;
padding: 0 10px;
&.img {
.item_wrap {
width: 25%;
}
}
.item_wrap {
width: 20%;
padding: 0 10px;
padding-bottom: 20px;
}
.item {
border: 1px solid #ececec;
border-radius: 10px;
overflow: hidden;
position: relative;
&:hover {
cursor: pointer;
}
.dot {
padding: 0 8px;
background-color: var(--el-color-danger);
color: #fff;
border-radius: 20px;
position: absolute;
top: 4px;
right: 4px;
z-index: 1;
font-size: 12px;
}
.cover {
width: 100%;
height: 300px;
}
.name {
padding: 14px 10px;
span {
font-weight: bold;
font-size: 18px;
}
}
.empty {
height: 50px;
}
.price {
padding: 10px 20px;
background-color: var(--primary-color);
span {
color: #fff;
font-weight: bold;
font-size: 18px;
}
}
}
}
</style>

View File

@ -92,9 +92,10 @@
</div>
<div class="footer">
<div class="top">
<div class="left">
<div class="left" @click="allSelected = !allSelected">
<div class="selected">
<el-icon class="icon">
<div class="selected_round" v-if="!allSelected"></div>
<el-icon class="icon" v-else>
<CircleCheckFilled />
</el-icon>
</div>
@ -118,73 +119,31 @@
</div>
</div>
<div class="shop_manage card">
<div class="header">
<div class="menus">
<div class="item active">
<el-text>全部</el-text>
</div>
<div class="item">
<el-text>咖啡</el-text>
</div>
<div class="item">
<el-text>测试</el-text>
</div>
<div class="item">
<el-text>套餐</el-text>
</div>
</div>
<div class="all">
<el-icon class="icon">
<Menu />
</el-icon>
</div>
</div>
<div class="search_wrap">
<div class="input">
<el-input placeholder="商品名称或首字母简称" prefix-icon="Search"></el-input>
</div>
<el-button :icon="shopListType == 'text' ? 'PictureRounded' : 'PriceTag'" @click="changeShopListType"></el-button>
</div>
<div class="shop_list" :class="{ img: shopListType == 'img' }">
<div class="item_wrap" v-for="item in 7" :key="item">
<div class="item">
<div class="dot">2</div>
<div class="cover" v-if="shopListType == 'img'">
<el-image src="https://img1.baidu.com/it/u=2183780444,2807225961&fm=253&fmt=auto&app=138&f=JPEG?w=600&h=400"
style="width: 100%;height: 100%;" fit="cover"></el-image>
</div>
<div class="name"><el-text>酱香拿铁</el-text></div>
<div class="empty" v-if="shopListType == 'text'"></div>
<div class="price">
<el-text>0.03</el-text>
</div>
</div>
</div>
</div>
<!-- 分类/商品列表 -->
<goods />
</div>
</div>
<!-- 选择规格 -->
<skuModal />
<!-- 备注 -->
<remarkModal ref="remarkRef" />
<!-- 修改取餐号 -->
<takeFoodCode />
</template>
<script setup>
import { ref } from 'vue'
import skuModal from '@/components/skuModal.vue'
import remarkModal from '@/components/remarkModal.vue'
import takeFoodCode from '@/components/takeFoodCode.vue'
import { ref } from 'vue'
//
import goods from '@/views/home/components/goods.vue'
const remarkRef = ref(null)
const shopListType = ref('text')
//
function changeShopListType() {
if (shopListType.value == 'text') {
shopListType.value = 'img'
} else {
shopListType.value = 'text'
}
}
const allSelected = ref(false)
</script>
<style scoped lang="scss">
@ -351,15 +310,29 @@ function changeShopListType() {
align-items: center;
.selected {
width: 20px;
height: 20px;
display: flex;
align-items: center;
position: relative;
top: 1px;
.icon {
display: block;
font-size: 18px;
font-size: 20px;
color: var(--primary-color);
}
.selected_round {
width: 100%;
height: 100%;
border-radius: 50%;
border: 1px solid #ddd;
}
}
.t {
margin-left: 4px;
margin-left: 6px;
}
}
@ -405,146 +378,5 @@ function changeShopListType() {
flex: 1;
margin-left: 20px;
height: 100%;
.header {
display: flex;
height: 80px;
justify-content: space-between;
border-bottom: 1px solid #ececec;
.menus {
display: flex;
padding: 0 10px;
.item {
padding: 0 10px;
display: flex;
align-items: center;
position: relative;
span {
font-size: 24px;
}
&.active {
&::after {
content: "";
width: 70%;
height: 2px;
border-radius: 4px;
position: absolute;
bottom: 0;
left: 15%;
background-color: var(--primary-color);
}
span {
color: #333;
}
}
}
}
.all {
width: 80px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
&::before {
content: "";
height: 60%;
border-left: 1px solid #ececec;
position: absolute;
left: 0;
top: 20%;
}
.icon {
color: #555;
font-size: 20px;
}
}
}
.search_wrap {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 20px;
}
.shop_list {
max-height: calc(100vh - 40px - 80px - 80px);
overflow-y: auto;
display: flex;
flex-wrap: wrap;
padding: 0 10px;
&.img {
.item_wrap {
width: 25%;
}
}
.item_wrap {
width: 20%;
padding: 0 10px;
padding-bottom: 20px;
}
.item {
border: 1px solid #ececec;
border-radius: 10px;
overflow: hidden;
position: relative;
&:hover {
cursor: pointer;
}
.dot {
padding: 0 8px;
background-color: var(--el-color-danger);
color: #fff;
border-radius: 20px;
position: absolute;
top: 4px;
right: 4px;
z-index: 1;
font-size: 12px;
}
.cover {
width: 100%;
height: 300px;
}
.name {
padding: 14px 10px;
span {
font-weight: bold;
font-size: 18px;
}
}
.empty {
height: 50px;
}
.price {
padding: 10px 20px;
background-color: var(--primary-color);
span {
color: #fff;
font-weight: bold;
font-size: 18px;
}
}
}
}
}
</style>

View File

@ -4,41 +4,24 @@
<el-image :src="logo" style="width: 180px"></el-image>
</div>
<div class="form-wrap">
<div class="reg-wrap">
<!-- <div class="reg-wrap">
<router-link :to="{ name: 'register' }">
<el-link type="primary">注册</el-link>
</router-link>
</div>
</div> -->
<div class="header">
<span class="t1">银收客</span>
<span class="t2">收银库存营销支付等业务一体化解决方案</span>
</div>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-position="top"
size="large"
hide-required-asterisk
>
<el-form-item label="注册商户号" prop="shopCode">
<el-input
v-model="form.shopCode"
placeholder="请输入注册商户号"
></el-input>
</el-form-item>
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" hide-required-asterisk>
<!-- <el-form-item label="注册商户号" prop="shopCode">
<el-input v-model="form.shopCode" placeholder="请输入注册商户号"></el-input>
</el-form-item> -->
<el-form-item label="手机号码" prop="phone">
<el-input
v-model="form.phone"
placeholder="请输入11位手机号码"
></el-input>
<el-input v-model="form.phone" placeholder="请输入11位手机号码"></el-input>
</el-form-item>
<el-form-item label="登录密码" prop="password">
<el-input
v-model="form.password"
type="password"
placeholder="请输入登录密码"
></el-input>
<el-input v-model="form.password" type="password" placeholder="请输入登录密码"></el-input>
</el-form-item>
<el-form-item>
<div style="width: 100%; display: flex; justify-content: flex-end">
@ -48,12 +31,7 @@
</div>
</el-form-item>
<el-form-item>
<el-button
type="primary"
style="width: 100%"
:loading="loading"
@click="submitHandle"
>
<el-button type="primary" style="width: 100%" :loading="loading" @click="submitHandle">
登录
</el-button>
</el-form-item>
@ -76,10 +54,16 @@ import { reactive, ref } from "vue";
import { useRouter } from "vue-router";
import { ipcRenderer } from "electron";
import { RandomNumBoth } from '@/utils'
import { useUser } from "@/store/user.js";
const store = useUser();
const router = useRouter();
const formRef = ref(null);
const loading = ref(false);
const form = reactive({
shopCode: "",
phone: "",
@ -112,16 +96,26 @@ const rules = reactive({
//
const submitHandle = () => {
formRef.value.validate((valid) => {
formRef.value.validate(async (valid) => {
if (valid) {
const params = {
serialNumber: RandomNumBoth(1000, 9999),
clientType: 'pc',
loginName: form.phone,
password: form.password
}
store.userlogin(params).then((res) => {
loading.value = true;
ElMessage.success("登录成功");
localStorage.setItem("token", "skk918sjakajhjjqhw19jsdkandkahk");
setTimeout(() => {
router.replace({
name: "home",
});
}, 1500);
}, 1000);
}).catch(err => {
loading.value = false
});
}
});
};
@ -137,8 +131,11 @@ const logout = () => {
<style scoped lang="scss">
.login-container {
width: 100vw;
display: flex;
height: 100vh;
background-color: #efefef;
.logo {
flex: 1;
height: inherit;
@ -147,30 +144,36 @@ const logout = () => {
align-items: center;
justify-content: center;
}
.form-wrap {
flex: 1;
flex: 1.5;
height: inherit;
padding: 50px;
}
.reg-wrap {
display: flex;
justify-content: flex-end;
padding-bottom: 100px;
}
.header {
display: flex;
flex-direction: column;
padding-bottom: 100px;
.t1 {
font-size: 52px;
color: #999;
}
.t2 {
font-size: 24px;
color: #999;
}
}
}
.version {
padding-top: 50px;
display: flex;

View File

@ -6,6 +6,15 @@ import path from "path";
// https://vitejs.dev/config/
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'https://cashierclient.sxczgkj.cn/cashier-client',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
plugins: [
vue(),
electron({