This commit is contained in:
weipengfei 2024-05-25 15:15:08 +08:00
parent 6a86e5fa99
commit 0181a84b01
22 changed files with 1288 additions and 251 deletions

View File

@ -3,8 +3,8 @@ VITE_NOW_TYPE = 'dist'
# Base API # Base API
# VITE_APP_BASE_URL='http://192.168.1.13:8546' # VITE_APP_BASE_URL='http://192.168.1.13:8546'
# VITE_PUSH_URL = 'ws://192.168.1.22:8787' VITE_PUSH_URL = 'ws://192.168.1.22:8787'
# VITE_APP_BASE_URL = 'http://192.168.1.22:8546' VITE_APP_BASE_URL = 'http://192.168.1.22:8546'
VITE_PUSH_URL ='wss://erp.lihaink.cn/pull' # VITE_PUSH_URL ='wss://erp.lihaink.cn/pull'
VITE_APP_BASE_URL='https://erp.lihaink.cn' # VITE_APP_BASE_URL='https://erp.lihaink.cn'

23
src/api/quote.ts Normal file
View File

@ -0,0 +1,23 @@
import request from "@/utils/request";
/**
* @description
*/
export function opurchaseGoodsOfferDateListsApi(params: any) {
return request.get({ url: "/operation/OpurchaseGoodsOffer/date_lists", params });
}
/**
* @description
*/
export function opurchaseGoodsOfferListsApi(params: any) {
return request.get({ url: "/operation/OpurchaseGoodsOffer/lists", params });
}
/**
* @description
*/
export function opurchaseGoodsOfferOfferApi(params: any) {
return request.post({ url: "/operation/OpurchaseGoodsOffer/offer", params });
}

View File

@ -1,6 +1,7 @@
export enum PageEnum { export enum PageEnum {
//登录页面 //登录页面
LOGIN = '/login', LOGIN = '/login',
ADMIN_LOGIN = '/admin_login',
//无权限页面 //无权限页面
ERROR_403 = '/403', ERROR_403 = '/403',
INDEX = '/' INDEX = '/'

View File

@ -13,6 +13,7 @@ export enum RequestMethodsEnum {
export enum RequestCodeEnum { export enum RequestCodeEnum {
SUCCESS = 1, SUCCESS = 1,
FAIL = 0, FAIL = 0,
SERVER_ERROR = 500,
LOGIN_FAILURE = -1, LOGIN_FAILURE = -1,
OPEN_NEW_PAGE = 2 OPEN_NEW_PAGE = 2
} }

View File

@ -20,6 +20,7 @@ import LayoutMain from './components/main.vue'
import LayoutSidebar from './components/sidebar/index.vue' import LayoutSidebar from './components/sidebar/index.vue'
import LayoutHeader from './components/header/index.vue' import LayoutHeader from './components/header/index.vue'
import { Push } from "@/common/push.js"; import { Push } from "@/common/push.js";
import { print } from "@/utils/print";
const connection = new Push({ const connection = new Push({
url: import.meta.env.VITE_PUSH_URL, // websocket url: import.meta.env.VITE_PUSH_URL, // websocket
@ -30,11 +31,63 @@ const connection = new Push({
const user_channel = connection.subscribe(`platform_1`); const user_channel = connection.subscribe(`platform_1`);
// const user_channel = connection.subscribe(`store_merchant_${1}`); // const user_channel = connection.subscribe(`store_merchant_${1}`);
// setTimeout(()=>{
// print(
// {
// id: 405,
// merchant: 501,
// real_name: '',
// user_phone: '19330904744',
// user_address: '',
// uid: '9',
// number: 'PF171617436315965155',
// total: '0.02',
// actual: '0.02',
// create_time: '2024-05-20 11:06:03',
// mer_name: '',
// mer_phone: '15566669999',
// mer_nickname: '',
// mer_user_mobile: '',
// nickname: '1532345',
// user_moblie: '19330904747',
// info: [
// {
// goods: 317,
// nums: '1.00',
// price: '0.02',
// total: '0.02',
// unit_name: '',
// goods_name: '西'
// },
// {
// goods: 317,
// nums: '1.56',
// price: '1.98',
// total: '3.09',
// unit_name: '',
// goods_name: ''
// },
// {
// goods: 317,
// nums: '2.58',
// price: '3.68',
// total: '9.49',
// unit_name: '',
// goods_name: ''
// }
// ]
// }
// )
// }, 1000)
// user-2message // user-2message
user_channel.on('message', function (data) { user_channel.on('message', function (data: any) {
console.log("收到消息--",data); console.log("收到消息--",data);
if(data.event=='platform_print'){ if(data.event=='platform_print'){
console.log('收到打印消息'); console.log('收到打印消息');
if(typeof data.data != 'object' || !data.data?.info?.length) return ElMessage.error('收到订单,但打印数据不合法,请联系管理员');
else print(data.data);
} }
}); });
// 线 // 线

View File

@ -12,20 +12,24 @@ import { PageEnum } from './enums/pageEnum'
import useTabsStore from './stores/modules/multipleTabs' import useTabsStore from './stores/modules/multipleTabs'
import { clearAuthInfo } from './utils/auth' import { clearAuthInfo } from './utils/auth'
import config from './config' import config from './config'
import cache from '@/utils/cache'
// NProgress配置 // NProgress配置
NProgress.configure({ showSpinner: false }) NProgress.configure({ showSpinner: false })
const loginPath = PageEnum.LOGIN const loginPath = PageEnum.LOGIN
const adminLoginPath = PageEnum.ADMIN_LOGIN
const defaultPath = PageEnum.INDEX const defaultPath = PageEnum.INDEX
// 免登录白名单 // 免登录白名单
const whiteList: string[] = [PageEnum.LOGIN, PageEnum.ERROR_403] const whiteList: string[] = [PageEnum.LOGIN, PageEnum.ERROR_403, PageEnum.ADMIN_LOGIN]
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
// 开始 Progress Bar // 开始 Progress Bar
NProgress.start() NProgress.start()
document.title = to.meta.title ?? config.title document.title = to.meta.title ?? config.title
const userStore = useUserStore() const userStore = useUserStore()
const tabsStore = useTabsStore() const tabsStore = useTabsStore()
const is_admin = cache.get('is_admin')
if (whiteList.includes(to.path)) { if (whiteList.includes(to.path)) {
// 在免登录白名单,直接进入 // 在免登录白名单,直接进入
next() next()
@ -33,7 +37,7 @@ router.beforeEach(async (to, from, next) => {
// 获取用户信息 // 获取用户信息
const hasGetUserInfo = Object.keys(userStore.userInfo).length !== 0 const hasGetUserInfo = Object.keys(userStore.userInfo).length !== 0
if (hasGetUserInfo) { if (hasGetUserInfo) {
if (to.path === loginPath) { if (to.path === loginPath || to.path === adminLoginPath) {
next({ path: defaultPath }) next({ path: defaultPath })
} else { } else {
next() next()
@ -70,11 +74,15 @@ router.beforeEach(async (to, from, next) => {
next({ ...to, replace: true }) next({ ...to, replace: true })
} catch (err) { } catch (err) {
clearAuthInfo() clearAuthInfo()
next({ path: loginPath, query: { redirect: to.fullPath } }) if (is_admin==1) {
next({ path: adminLoginPath, query: { redirect: to.fullPath } })
} else next({ path: loginPath, query: { redirect: to.fullPath } })
} }
} }
} else { } else {
next({ path: loginPath, query: { redirect: to.fullPath } }) if (is_admin==1) {
next({ path: adminLoginPath, query: { redirect: to.fullPath } })
} else next({ path: loginPath, query: { redirect: to.fullPath } })
} }
}) })

View File

@ -34,6 +34,10 @@ export const constantRoutes: Array<RouteRecordRaw> = [
path: PageEnum.LOGIN, path: PageEnum.LOGIN,
component: () => import('@/views/account/login.vue') component: () => import('@/views/account/login.vue')
}, },
{
path: PageEnum.ADMIN_LOGIN,
component: () => import('@/views/account/admin_login.vue')
},
{ {
path: '/user', path: '/user',
component: LAYOUT, component: LAYOUT,

View File

@ -32,15 +32,17 @@ const useUserStore = defineStore({
this.perms = [] this.perms = []
}, },
login(playload: any) { login(playload: any) {
const { account, password } = playload const { account, password, is_admin } = playload
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
login({ login({
account: account.trim(), account: account.trim(),
password: password password: password,
is_admin: is_admin
}) })
.then((data) => { .then((data) => {
this.token = data.token this.token = data.token
cache.set(TOKEN_KEY, data.token) cache.set(TOKEN_KEY, data.token)
cache.set('is_admin', is_admin)
resolve(data) resolve(data)
}) })
.catch((error) => { .catch((error) => {
@ -53,7 +55,8 @@ const useUserStore = defineStore({
logout() logout()
.then(async (data) => { .then(async (data) => {
this.token = '' this.token = ''
await router.push(PageEnum.LOGIN) const is_admin = cache.get('is_admin')
await router.push(is_admin==1 ? PageEnum.ADMIN_LOGIN : PageEnum.LOGIN)
clearAuthInfo() clearAuthInfo()
resolve(data) resolve(data)
}) })

View File

@ -16,50 +16,188 @@ export const print = (data: any) => {
const list = hiprint.hiwebSocket.getPrinterList(); const list = hiprint.hiwebSocket.getPrinterList();
console.log(list); console.log(list);
let nowHeight = 0;
let textHeight = 10;
// 下列方法都是没有拖拽设计页面的, 相当于代码模式, 使用代码设计页面 // 下列方法都是没有拖拽设计页面的, 相当于代码模式, 使用代码设计页面
// 想要实现拖拽设计页面,请往下看 '自定义设计' // 想要实现拖拽设计页面,请往下看 '自定义设计'
var hiprintTemplate = new hiprint.PrintTemplate(); var hiprintTemplate = new hiprint.PrintTemplate();
// 纸张高度 固定为 130 pt + 商品数量高度
let oneHeight = 120 + Math.ceil((data.info.length * 2 * 10) / 2.84);
// 模板宽度单位是mm ( 1mm ~= 2.84pt ) 其他的宽高单位是pt // 模板宽度单位是mm ( 1mm ~= 2.84pt ) 其他的宽高单位是pt
var panel = hiprintTemplate.addPrintPanel({ var panel = hiprintTemplate.addPrintPanel({
width: WIDTH, // 58mm = 164pt width: 58, // 58mm = 164pt
height: 58, height: oneHeight,
paperNumberDisabled: true, paperNumberDisabled: true,
topOffset: -1,
}); });
//文本 let options = (e: any) => {
panel.addPrintText({ nowHeight += textHeight;
options: { let opt = {
width: T_WIDTH, width: T_WIDTH,
height: 10, height: textHeight,
top: 10, top: nowHeight,
left: T_LEFT, left: T_LEFT,
title: "hiprint插件手动添加text", title: "",
};
if (typeof e === "string") opt.title = e;
else opt = Object.assign(opt, e);
return {
options: opt,
};
};
let lineTitle = () => {
nowHeight += textHeight;
let left = Math.floor(T_WIDTH / 3);
panel.addPrintText({
options: {
width: left,
height: 10,
top: nowHeight,
left: 0,
title: "单价",
textAlign: "center",
},
});
panel.addPrintText({
options: {
width: left,
height: textHeight,
top: nowHeight,
left: left,
title: "数量",
textAlign: "center",
},
});
panel.addPrintText({
options: {
width: left,
height: textHeight,
top: nowHeight,
left: left * 2,
title: "小计",
textAlign: "center",
},
});
};
let lineOptions = (text1: any, text2: any, text3: any) => {
nowHeight += textHeight;
let left = Math.floor(T_WIDTH / 3);
panel.addPrintText({
options: {
width: left,
height: 10,
top: nowHeight,
left: 0,
title: text1 + "",
textAlign: "center",
},
});
panel.addPrintText({
options: {
width: left,
height: textHeight,
top: nowHeight,
left: left,
title: text2 + "",
textAlign: "center",
},
});
panel.addPrintText({
options: {
width: left,
height: textHeight,
top: nowHeight,
left: left * 2,
title: text3 + "",
textAlign: "center",
},
});
};
//文本 *1
panel.addPrintText(
options({
title: "泸优采-采购单",
textAlign: "center", textAlign: "center",
}, })
);
//文本 *6
panel.addPrintText(options("单号: " + data.number));
panel.addPrintText(options("配送时间: " + data.create_time));
panel.addPrintText(options("配送员: " + data.mer_nickname));
panel.addPrintText(options("配送电话: " + data.mer_phone));
panel.addPrintText(options("======================"));
panel.addPrintText(options("商品信息: "));
//格式化 *1 + x * y
lineTitle();
data.info.forEach((item: any) => {
panel.addPrintText(options(item.goods_name));
if(item.nums==Math.floor(+item.nums)) item.nums = Number(item.nums).toFixed(0);
lineOptions(
`${item.price}`,
`${item.nums}${item.unit_name}`,
`${item.total}`
);
}); });
//长文本
panel.addPrintLongText({ //文本 *5
panel.addPrintText(options("======================"));
panel.addPrintText(options(`合计: ${data.total}`));
panel.addPrintText(options("提货点: " + data.mer_name));
panel.addPrintText(options("提货点电话: " + data.mer_phone));
panel.addPrintText(options("提货点负责人签字:"));
// https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png //无字
// https://lihai001.oss-cn-chengdu.aliyuncs.com/def/54705202405221504133485.png //有字
// panel.addPrintRect({ options: { width: T_WIDTH, height:30,top: nowHeight + 15, left: T_LEFT,borderColor:'',borderWidth:0.75 } });
// nowHeight+=40;
// 文本 *6
panel.addPrintImage({
options: { options: {
width: T_WIDTH, width: T_WIDTH,
height: 35, height: 50,
top: 30, top: nowHeight + 15,
left: T_LEFT, left: T_LEFT,
title: "长文本hiprint是一个很好的webjs打印,浏览器在的地方他都可以运行", title: "",
src: "https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png",
}, },
}); });
//长文本 nowHeight += 60;
panel.addPrintLongText({ // 文本 *4
panel.addPrintText(options("收货人: " + data.real_name));
panel.addPrintText(options("收货地址: " + data.user_address));
panel.addPrintText(options("联系电话: " + data.user_phone));
panel.addPrintText(options("收货人签字:"));
// panel.addPrintRect({ options: { width: T_WIDTH, height:30,top: nowHeight+15, left: T_LEFT,borderColor:'',borderWidth:0.75 } });
// 文本 *6
panel.addPrintImage({
options: { options: {
width: T_WIDTH, width: T_WIDTH,
height: 35, height: 50,
top: 80, top: nowHeight + 15,
left: T_LEFT, left: T_LEFT,
title: title: "",
"长文本:直接打印,需要安装客户端, 直接打印,需要安装客户端, 直接打印,需要安装客户端, 直接打印,需要安装客户端", src: "https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png",
}, },
}); });
nowHeight += 60;
// 文本 *4
panel.addPrintText(options(""));
panel.addPrintText(options(""));
panel.addPrintText(options(""));
panel.addPrintText(options("======================"));
// 合计高度 23 + length * 2
//打印 //打印
hiprintTemplate.print({}); hiprintTemplate.print({});
//直接打印,需要安装客户端 //直接打印,需要安装客户端
@ -94,25 +232,25 @@ export const testPrint = () => {
paperNumberDisabled: true, paperNumberDisabled: true,
}); });
let options = (e: any)=>{ let options = (e: any) => {
nowHeight+=textHeight; nowHeight += textHeight;
let opt = { let opt = {
width: T_WIDTH, width: T_WIDTH,
height: textHeight, height: textHeight,
top: nowHeight, top: nowHeight,
left: T_LEFT, left: T_LEFT,
title: '', title: "",
} };
if(typeof e === 'string') opt.title = e; if (typeof e === "string") opt.title = e;
else opt = Object.assign(opt, e); else opt = Object.assign(opt, e);
return { return {
options: opt options: opt,
} };
} };
let lineTitle = ()=>{ let lineTitle = () => {
nowHeight+=textHeight; nowHeight += textHeight;
let left = Math.floor(T_WIDTH/3) ; let left = Math.floor(T_WIDTH / 3);
panel.addPrintText({ panel.addPrintText({
options: { options: {
width: left, width: left,
@ -143,18 +281,18 @@ export const testPrint = () => {
textAlign: "center", textAlign: "center",
}, },
}); });
} };
let lineOptions = (text1: any, text2: any, text3: any)=>{ let lineOptions = (text1: any, text2: any, text3: any) => {
nowHeight+=textHeight; nowHeight += textHeight;
let left = Math.floor(T_WIDTH/3) ; let left = Math.floor(T_WIDTH / 3);
panel.addPrintText({ panel.addPrintText({
options: { options: {
width: left, width: left,
height: 10, height: 10,
top: nowHeight, top: nowHeight,
left: 0, left: 0,
title: text1+"", title: text1 + "",
textAlign: "center", textAlign: "center",
}, },
}); });
@ -164,7 +302,7 @@ export const testPrint = () => {
height: textHeight, height: textHeight,
top: nowHeight, top: nowHeight,
left: left, left: left,
title: text2+"", title: text2 + "",
textAlign: "center", textAlign: "center",
}, },
}); });
@ -174,17 +312,19 @@ export const testPrint = () => {
height: textHeight, height: textHeight,
top: nowHeight, top: nowHeight,
left: left * 2, left: left * 2,
title: text3+"", title: text3 + "",
textAlign: "center", textAlign: "center",
}, },
}); });
} };
//文本 *1 //文本 *1
panel.addPrintText(options({ panel.addPrintText(
title: '泸优采-小票0', options({
textAlign: "center", title: "泸优采-小票0",
})); textAlign: "center",
})
);
//文本 *6 //文本 *6
panel.addPrintText(options("单号: PF171617436315965155")); panel.addPrintText(options("单号: PF171617436315965155"));
@ -205,11 +345,11 @@ export const testPrint = () => {
//文本 *13 //文本 *13
panel.addPrintText(options("======================")); panel.addPrintText(options("======================"));
panel.addPrintText(options('合计: 0.75元')); panel.addPrintText(options("合计: 0.75元"));
panel.addPrintText(options('提货点: 莲花农贸市场')); panel.addPrintText(options("提货点: 莲花农贸市场"));
panel.addPrintText(options('提货点电话: 19330904744')); panel.addPrintText(options("提货点电话: 19330904744"));
panel.addPrintText(options('提货点负责人签字:')); panel.addPrintText(options("提货点负责人签字:"));
// https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png //无字 // https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png //无字
// https://lihai001.oss-cn-chengdu.aliyuncs.com/def/54705202405221504133485.png //有字 // https://lihai001.oss-cn-chengdu.aliyuncs.com/def/54705202405221504133485.png //有字
// panel.addPrintRect({ options: { width: T_WIDTH, height:30,top: nowHeight + 15, left: T_LEFT,borderColor:'',borderWidth:0.75 } }); // panel.addPrintRect({ options: { width: T_WIDTH, height:30,top: nowHeight + 15, left: T_LEFT,borderColor:'',borderWidth:0.75 } });
@ -217,38 +357,37 @@ export const testPrint = () => {
panel.addPrintImage({ panel.addPrintImage({
options: { options: {
width: T_WIDTH, width: T_WIDTH,
height:50, height: 50,
top: nowHeight + 15, top: nowHeight + 15,
left: T_LEFT, left: T_LEFT,
title: '', title: "",
src: 'https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png' src: "https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png",
} },
}) });
nowHeight+=60; nowHeight += 60;
panel.addPrintText(options('收货人: 阿哈')); panel.addPrintText(options("收货人: 阿哈"));
panel.addPrintText(options('收货地址: 里海科技')); panel.addPrintText(options("收货地址: 里海科技"));
panel.addPrintText(options('联系电话: 17685151643')); panel.addPrintText(options("联系电话: 17685151643"));
panel.addPrintText(options('收货人签字:')); panel.addPrintText(options("收货人签字:"));
// panel.addPrintRect({ options: { width: T_WIDTH, height:30,top: nowHeight+15, left: T_LEFT,borderColor:'',borderWidth:0.75 } }); // panel.addPrintRect({ options: { width: T_WIDTH, height:30,top: nowHeight+15, left: T_LEFT,borderColor:'',borderWidth:0.75 } });
panel.addPrintImage({ panel.addPrintImage({
options: { options: {
width: T_WIDTH, width: T_WIDTH,
height: 50, height: 50,
top: nowHeight + 15, top: nowHeight + 15,
left: T_LEFT, left: T_LEFT,
title: '', title: "",
src: 'https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png' src: "https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png",
} },
}) });
nowHeight+=60; nowHeight += 60;
panel.addPrintText(options("")); panel.addPrintText(options(""));
panel.addPrintText(options("")); panel.addPrintText(options(""));
panel.addPrintText(options("")); panel.addPrintText(options(""));
panel.addPrintText(options("======================")); panel.addPrintText(options("======================"));
// 合计高度 23 + length * 2 // 合计高度 23 + length * 2
//打印 //打印
// hiprintTemplate.print({}); // hiprintTemplate.print({});
@ -258,22 +397,24 @@ export const testPrint = () => {
// 发送任务到打印机成功 // 发送任务到打印机成功
hiprintTemplate.on("printSuccess", (e: any) => { hiprintTemplate.on("printSuccess", (e: any) => {
console.log("printSuccess", e); console.log("printSuccess", e);
ElMessage.success('订单已加入打印队列');
}); });
// 发送任务到打印机失败 // 发送任务到打印机失败
hiprintTemplate.on("printError", (e: any) => { hiprintTemplate.on("printError", (e: any) => {
console.log("printError", e); console.log("printError", e);
ElMessage.error('打印失败,请检查是否正确连接打印机!');
}); });
}; };
export const printerList = ()=>{ export const printerList = () => {
try{ try {
const list = hiprint.hiwebSocket.getPrinterList(); const list = hiprint.hiwebSocket.getPrinterList();
return list; return list;
}catch{ } catch {
ElMessage.error("请先安装打印机客户端") ElMessage.error("请先安装打印机客户端");
return []; return [];
} }
} };
// 计算字符串长度 // 计算字符串长度
function calculateStringLength(str: string) { function calculateStringLength(str: string) {

View File

@ -1,123 +1,133 @@
import { merge } from 'lodash' import { merge } from "lodash";
import configs from '@/config' import configs from "@/config";
import { Axios } from './axios' import { Axios } from "./axios";
import { ContentTypeEnum, RequestCodeEnum, RequestMethodsEnum } from '@/enums/requestEnums' import {
import type { AxiosHooks } from './type' ContentTypeEnum,
import { clearAuthInfo, getToken } from '../auth' RequestCodeEnum,
import feedback from '../feedback' RequestMethodsEnum,
import NProgress from 'nprogress' } from "@/enums/requestEnums";
import { AxiosError, type AxiosRequestConfig } from 'axios' import type { AxiosHooks } from "./type";
import router from '@/router' import { clearAuthInfo, getToken } from "../auth";
import { PageEnum } from '@/enums/pageEnum' import feedback from "../feedback";
import NProgress from "nprogress";
import { AxiosError, type AxiosRequestConfig } from "axios";
import router from "@/router";
import { PageEnum } from "@/enums/pageEnum";
// 处理axios的钩子函数 // 处理axios的钩子函数
const axiosHooks: AxiosHooks = { const axiosHooks: AxiosHooks = {
requestInterceptorsHook(config) { requestInterceptorsHook(config) {
NProgress.start() NProgress.start();
const { withToken, isParamsToData } = config.requestOptions const { withToken, isParamsToData } = config.requestOptions;
const params = config.params || {} const params = config.params || {};
const headers = config.headers || {} const headers = config.headers || {};
// 添加token // 添加token
if (withToken) { if (withToken) {
const token = getToken() const token = getToken();
headers.token = token headers.token = token;
}
// POST请求下如果无data则将params视为data
if (
isParamsToData &&
!Reflect.has(config, 'data') &&
config.method?.toUpperCase() === RequestMethodsEnum.POST
) {
config.data = params
config.params = {}
}
config.headers = headers
return config
},
requestInterceptorsCatchHook(err) {
NProgress.done()
return err
},
async responseInterceptorsHook(response) {
NProgress.done()
const { isTransformResponse, isReturnDefaultResponse } = response.config.requestOptions
//返回默认响应,当需要获取响应头及其他数据时可使用
if (isReturnDefaultResponse) {
return response
}
// 是否需要对数据进行处理
if (!isTransformResponse) {
return response.data
}
const { code, data, show, msg } = response.data
switch (code) {
case RequestCodeEnum.SUCCESS:
if (show) {
msg && feedback.msgSuccess(msg)
}
return data
case RequestCodeEnum.FAIL:
if (show) {
msg && feedback.msgError(msg)
}
return Promise.reject(data)
case RequestCodeEnum.LOGIN_FAILURE:
clearAuthInfo()
router.push(PageEnum.LOGIN)
return Promise.reject()
case RequestCodeEnum.OPEN_NEW_PAGE:
window.location.href = data.url
return data
default:
return data
}
},
responseInterceptorsCatchHook(error) {
NProgress.done()
if (error.code !== AxiosError.ERR_CANCELED) {
error.message && feedback.msgError(error.message)
}
return Promise.reject(error)
} }
} // POST请求下如果无data则将params视为data
if (
isParamsToData &&
!Reflect.has(config, "data") &&
config.method?.toUpperCase() === RequestMethodsEnum.POST
) {
config.data = params;
config.params = {};
}
config.headers = headers;
return config;
},
requestInterceptorsCatchHook(err) {
NProgress.done();
return err;
},
async responseInterceptorsHook(response) {
NProgress.done();
const { isTransformResponse, isReturnDefaultResponse } =
response.config.requestOptions;
//返回默认响应,当需要获取响应头及其他数据时可使用
if (isReturnDefaultResponse) {
return response;
}
// 是否需要对数据进行处理
if (!isTransformResponse) {
return response.data;
}
const { code, data, show, msg } = response.data;
switch (code) {
case RequestCodeEnum.SUCCESS:
if (show) {
msg && feedback.msgSuccess(msg);
}
return data;
case RequestCodeEnum.FAIL:
if (show) {
msg && feedback.msgError(msg);
}
return Promise.reject(data);
case RequestCodeEnum.SERVER_ERROR:
if (show) {
msg && feedback.msgError(msg);
}
return Promise.reject(data);
case RequestCodeEnum.LOGIN_FAILURE:
clearAuthInfo();
router.push(PageEnum.LOGIN);
return Promise.reject();
case RequestCodeEnum.OPEN_NEW_PAGE:
window.location.href = data.url;
return data;
default:
return data;
}
},
responseInterceptorsCatchHook(error) {
NProgress.done();
if (error.code !== AxiosError.ERR_CANCELED) {
error.message && feedback.msgError(error.message);
}
return Promise.reject(error);
},
};
const defaultOptions: AxiosRequestConfig = { const defaultOptions: AxiosRequestConfig = {
//接口超时时间 //接口超时时间
timeout: configs.timeout, timeout: configs.timeout,
// 基础接口地址 // 基础接口地址
baseURL: configs.baseUrl, baseURL: configs.baseUrl,
//请求头 //请求头
headers: { 'Content-Type': ContentTypeEnum.JSON, version: configs.version }, headers: { "Content-Type": ContentTypeEnum.JSON, version: configs.version },
// 处理 axios的钩子函数 // 处理 axios的钩子函数
axiosHooks: axiosHooks, axiosHooks: axiosHooks,
// 每个接口可以单独配置 // 每个接口可以单独配置
requestOptions: { requestOptions: {
// 是否将params视为data参数仅限post请求 // 是否将params视为data参数仅限post请求
isParamsToData: true, isParamsToData: true,
//是否返回默认的响应 //是否返回默认的响应
isReturnDefaultResponse: false, isReturnDefaultResponse: false,
// 需要对返回数据进行处理 // 需要对返回数据进行处理
isTransformResponse: true, isTransformResponse: true,
// 接口拼接地址 // 接口拼接地址
urlPrefix: configs.urlPrefix, urlPrefix: configs.urlPrefix,
// 忽略重复请求 // 忽略重复请求
ignoreCancelToken: false, ignoreCancelToken: false,
// 是否携带token // 是否携带token
withToken: true, withToken: true,
// 开启请求超时重新发起请求请求机制 // 开启请求超时重新发起请求请求机制
isOpenRetry: true, isOpenRetry: true,
// 重新请求次数 // 重新请求次数
retryCount: 2 retryCount: 2,
} },
} };
function createAxios(opt?: Partial<AxiosRequestConfig>) { function createAxios(opt?: Partial<AxiosRequestConfig>) {
return new Axios( return new Axios(
// 深度合并 // 深度合并
merge(defaultOptions, opt || {}) merge(defaultOptions, opt || {})
) );
} }
const request = createAxios() const request = createAxios();
export default request export default request;

View File

@ -0,0 +1,131 @@
<template>
<div class="login flex flex-col">
<div class="flex-1 flex items-center justify-center">
<div class="login-card flex rounded-md">
<div class="flex-1 h-full hidden md:inline-block">
<image-contain :src="config.login_image" :width="400" height="100%" />
</div>
<div
class="login-form bg-body flex flex-col justify-center px-10 py-10 md:w-[400px] w-[375px] flex-none mx-auto"
>
<div class="text-center text-3xl font-medium mb-8">{{ '里海ERP后台管理系统' || config.web_name }}</div>
<el-form ref="formRef" :model="formData" size="large" :rules="rules">
<el-form-item prop="account">
<el-input
v-model="formData.account"
placeholder="请输入账号"
@keyup.enter="handleEnter"
>
<template #prepend>
<icon name="el-icon-User" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
ref="passwordRef"
v-model="formData.password"
show-password
placeholder="请输入密码"
@keyup.enter="handleLogin"
>
<template #prepend>
<icon name="el-icon-Lock" />
</template>
</el-input>
</el-form-item>
</el-form>
<div class="mb-5">
<el-checkbox v-model="remAccount" label="记住账号"></el-checkbox>
</div>
<el-button type="primary" size="large" :loading="isLock" @click="lockLogin">
登录
</el-button>
</div>
</div>
</div>
<layout-footer />
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, reactive, ref, shallowRef } from 'vue'
import type { InputInstance, FormInstance } from 'element-plus'
import LayoutFooter from '@/layout/components/footer.vue'
import useAppStore from '@/stores/modules/app'
import useUserStore from '@/stores/modules/user'
import cache from '@/utils/cache'
import { ACCOUNT_KEY } from '@/enums/cacheEnums'
import { PageEnum } from '@/enums/pageEnum'
import { useLockFn } from '@/hooks/useLockFn'
const passwordRef = shallowRef<InputInstance>()
const formRef = shallowRef<FormInstance>()
const appStore = useAppStore()
const userStore = useUserStore()
const route = useRoute()
const router = useRouter()
const remAccount = ref(false)
const config = computed(() => appStore.config)
const formData = reactive({
account: '',
password: '',
is_admin: 1 //1, 0
})
const rules = {
account: [
{
required: true,
message: '请输入账号',
trigger: ['blur']
}
],
password: [
{
required: true,
message: '请输入密码',
trigger: ['blur']
}
]
}
//
const handleEnter = () => {
if (!formData.password) {
return passwordRef.value?.focus()
}
handleLogin()
}
//
const handleLogin = async () => {
await formRef.value?.validate()
//
cache.set(ACCOUNT_KEY, {
remember: remAccount.value,
account: remAccount.value ? formData.account : ''
})
await userStore.login(formData)
const {
query: { redirect }
} = route
const path = typeof redirect === 'string' ? redirect : PageEnum.INDEX
router.push(path)
}
const { isLock, lockFn: lockLogin } = useLockFn(handleLogin)
onMounted(() => {
const value = cache.get(ACCOUNT_KEY)
if (value?.remember) {
remAccount.value = value.remember
formData.account = value.account
}
})
</script>
<style lang="scss" scoped>
.login {
background-image: url('./images/login_bg.png');
@apply min-h-screen bg-no-repeat bg-center bg-cover;
.login-card {
height: 400px;
}
}
</style>

View File

@ -68,7 +68,8 @@ const remAccount = ref(false)
const config = computed(() => appStore.config) const config = computed(() => appStore.config)
const formData = reactive({ const formData = reactive({
account: '', account: '',
password: '' password: '',
is_admin: 0 //1, 0
}) })
const rules = { const rules = {
account: [ account: [

View File

@ -281,7 +281,7 @@
审核 审核
</el-button> </el-button>
<el-button <el-button
v-else-if="!row.status" v-else-if="!row.status && !row.mark"
link link
type="warning" type="warning"
@click="handleDetail(row)" @click="handleDetail(row)"

View File

@ -56,7 +56,7 @@
<el-button v-if="!row.status && (row.mark=='' || row.mark==null)" link type="primary" @click="handleDetail(row)"> <el-button v-if="!row.status && (row.mark=='' || row.mark==null)" link type="primary" @click="handleDetail(row)">
审核 审核
</el-button> </el-button>
<el-button v-else-if="!row.status" link type="warning" @click="handleDetail(row)"> <el-button v-else-if="!row.status && !row.mark" link type="warning" @click="handleDetail(row)">
重新审核 重新审核
</el-button> </el-button>
</template> </template>

View File

@ -5,23 +5,43 @@
<el-row> <el-row>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="供应商分类" prop="category_id"> <el-form-item label="供应商分类" prop="category_id">
<el-select v-model="queryParams.category_id" clearable placeholder="请选择供应商类型"> <el-select
<el-option v-for="(item, index) in dictData.mer_category_type" :key="index" :label="item.name" v-model="queryParams.category_id"
:value="parseInt(item.value)" /> clearable
placeholder="请选择供应商类型"
>
<el-option
v-for="(item, index) in dictData.mer_category_type"
:key="index"
:label="item.name"
:value="parseInt(item.value)"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="供应商类型" prop="type_id"> <el-form-item label="供应商类型" prop="type_id">
<el-select v-model="queryParams.type_id" clearable placeholder="请选择供应商类型"> <el-select
<el-option v-for="(item, index) in dictData.merchat_type" :key="index" :label="item.name" v-model="queryParams.type_id"
:value="parseInt(item.value)" /> clearable
placeholder="请选择供应商类型"
>
<el-option
v-for="(item, index) in dictData.merchat_type"
:key="index"
:label="item.name"
:value="parseInt(item.value)"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="供应商名称" prop="mer_name"> <el-form-item label="供应商名称" prop="mer_name">
<el-input v-model="queryParams.mer_name" clearable placeholder="请输入供应商名称" /> <el-input
v-model="queryParams.mer_name"
clearable
placeholder="请输入供应商名称"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
@ -34,13 +54,21 @@
</el-form> </el-form>
</el-card> </el-card>
<el-card class="!border-none" v-loading="pager.loading" shadow="never"> <el-card class="!border-none" v-loading="pager.loading" shadow="never">
<el-button v-perms="['supplier.supplier/add']" type="primary" @click="handleAdd"> <el-button
v-perms="['supplier.supplier/add']"
type="primary"
@click="handleAdd"
>
<template #icon> <template #icon>
<icon name="el-icon-Plus" /> <icon name="el-icon-Plus" />
</template> </template>
新增 新增
</el-button> </el-button>
<el-button v-perms="['supplier.supplier/delete']" :disabled="!selectData.length" @click="handleDelete(selectData)"> <el-button
v-perms="['supplier.supplier/delete']"
:disabled="!selectData.length"
@click="handleDelete(selectData)"
>
删除 删除
</el-button> </el-button>
<div class="mt-4"> <div class="mt-4">
@ -59,14 +87,33 @@
/> />
</template> </template>
</el-table-column> --> </el-table-column> -->
<el-table-column label="供应商类型" prop="type_id" show-overflow-tooltip> <el-table-column
label="供应商类型"
prop="type_id"
show-overflow-tooltip
>
<template #default="{ row }"> <template #default="{ row }">
<dict-value :options="dictData.merchat_type" :value="row.type_id" /> <dict-value
:options="dictData.merchat_type"
:value="row.type_id"
/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="供应商名称" prop="mer_name" show-overflow-tooltip /> <el-table-column
<el-table-column label="结算周期(天)" prop="settle_cycle" show-overflow-tooltip /> label="供应商名称"
<el-table-column label="利率" prop="interest_rate" show-overflow-tooltip /> prop="mer_name"
show-overflow-tooltip
/>
<el-table-column
label="结算周期(天)"
prop="settle_cycle"
show-overflow-tooltip
/>
<el-table-column
label="利率"
prop="interest_rate"
show-overflow-tooltip
/>
<!-- <el-table-column <!-- <el-table-column
label="标签" label="标签"
prop="sys_labels_arr" prop="sys_labels_arr"
@ -78,14 +125,26 @@
}}</span> }}</span>
</template> </template>
</el-table-column> --> </el-table-column> -->
<el-table-column label="供应商地址" prop="mer_address" show-overflow-tooltip /> <el-table-column
label="供应商地址"
prop="mer_address"
show-overflow-tooltip
/>
<el-table-column label="供应商是否禁用" prop="status"> <el-table-column label="供应商是否禁用" prop="status">
<template #default="{ row }"> <template #default="{ row }">
<dict-value :options="dictData.show_status" :value="row.status" /> <dict-value :options="dictData.show_status" :value="row.status" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="提成比例" prop="commission_rate" show-overflow-tooltip /> <el-table-column
<el-table-column label="供应商手续费单独设置" width="200" prop="commission_switch"> label="提成比例"
prop="commission_rate"
show-overflow-tooltip
/>
<el-table-column
label="供应商手续费单独设置"
width="200"
prop="commission_switch"
>
<template #default="{ row }"> <template #default="{ row }">
<!-- <dict-value :options="dictData.show_status" :value="row.commission_switch" /> --> <!-- <dict-value :options="dictData.show_status" :value="row.commission_switch" /> -->
{{ row.commission_switch ? "开启" : "关闭" }} {{ row.commission_switch ? "开启" : "关闭" }}
@ -98,22 +157,42 @@
/> />
<el-table-column label="操作" width="200" fixed="right"> <el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button v-perms="['supplier.supplier/edit']" type="primary" link @click="handleEdit(row)"> <el-button
v-perms="['supplier.supplier/edit']"
type="primary"
link
@click="handleEdit(row)"
>
编辑 编辑
</el-button> </el-button>
<el-button v-perms="['supplier.supplier/delete']" type="danger" link @click="handleDelete(row.id)"> <el-button
v-perms="['supplier.supplier/delete']"
type="danger"
link
@click="handleDelete(row.id)"
>
删除 删除
</el-button> </el-button>
<el-button link @click="handleDetail(row.id)"> 详情 </el-button> <el-button link @click="handleDetail(row.id)"> 详情 </el-button>
<router-link :to="{
path: 'bindGoods', <el-button link type="primary">
query: { <router-link
id: row.id, :to="{
}, path: 'bindGoods',
}"> query: {
<el-button link type="primary"> 商品绑定 </el-button> id: row.id,
</router-link> },
<el-button v-if="!row.uid" link @click="bindUser(row)" type="primary"> }"
>
商品绑定
</router-link>
</el-button>
<el-button
v-if="!row.uid"
link
@click="bindUser(row)"
type="primary"
>
绑定用户 绑定用户
</el-button> </el-button>
<el-button link @click="setBalance(row.id)"> 设置余额 </el-button> <el-button link @click="setBalance(row.id)"> 设置余额 </el-button>
@ -151,15 +230,17 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="金额" :label-width="100"> <el-form-item label="金额" :label-width="100">
<el-input v-model="balanceInfo.set_money" type="number" placeholder="请输入金额" /> <el-input
v-model="balanceInfo.set_money"
type="number"
placeholder="请输入金额"
/>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="showBalance = false">取消</el-button> <el-button @click="showBalance = false">取消</el-button>
<el-button type="primary" @click="submitBalance"> <el-button type="primary" @click="submitBalance"> 确认 </el-button>
确认
</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@ -174,7 +255,7 @@ import {
apiSupplierDelete, apiSupplierDelete,
apiSupplierDetail, apiSupplierDetail,
apiSupplierSetBalance, apiSupplierSetBalance,
apiSupplierBindUser apiSupplierBindUser,
} from "@/api/supplier"; } from "@/api/supplier";
import { timeFormat } from "@/utils/util"; import { timeFormat } from "@/utils/util";
import feedback from "@/utils/feedback"; import feedback from "@/utils/feedback";
@ -246,7 +327,7 @@ const handleDetail = async (id: any) => {
const showDialog = ref(false); const showDialog = ref(false);
const bindData = ref({ const bindData = ref({
id: "" id: "",
}); });
// //
const bindUser = (data: any) => { const bindUser = (data: any) => {
@ -261,7 +342,6 @@ const onBind = (data: any) => {
showDialog.value = false; showDialog.value = false;
getLists(); getLists();
}); });
}; };
const balanceInfo = ref({ const balanceInfo = ref({
@ -277,8 +357,9 @@ const setBalance = (id: any) => {
balanceInfo.value.type = 1; balanceInfo.value.type = 1;
showBalance.value = true; showBalance.value = true;
}; };
const submitBalance = ()=>{ const submitBalance = () => {
if(+balanceInfo.value.set_money <=0 || balanceInfo.value.set_money == '') return ElMessage.error('请输入正确的金额'); if (+balanceInfo.value.set_money <= 0 || balanceInfo.value.set_money == "")
return ElMessage.error("请输入正确的金额");
apiSupplierSetBalance({ apiSupplierSetBalance({
set_money: +balanceInfo.value.set_money, set_money: +balanceInfo.value.set_money,
id: balanceInfo.value.id, id: balanceInfo.value.id,
@ -287,7 +368,7 @@ const submitBalance = ()=>{
showBalance.value = false; showBalance.value = false;
getLists(); getLists();
}); });
} };
getLists(); getLists();
</script> </script>

View File

@ -0,0 +1,111 @@
<template>
<div class="edit-popup">
<popup ref="popupRef" :async="true" width="550px" @close="handleClose" :bottom-btn="false">
<el-descriptions class="margin-top" :title="popupTitle" :column="1" border>
<el-descriptions-item label="条码名称">
{{ formData.name }}
</el-descriptions-item>
<el-descriptions-item label="条码内容">
{{ formData.code }}
</el-descriptions-item>
<el-descriptions-item label="条码类型">
<dict-value :options="dictData.code_type" :value="formData.type" />
</el-descriptions-item>
<el-descriptions-item label="备注信息">
{{ formData.data }}
</el-descriptions-item>
<el-descriptions-item label="扩展信息">
{{ formData.more }}
</el-descriptions-item>
<el-descriptions-item label="排序">
{{ formData.sort }}
</el-descriptions-item>
</el-descriptions>
</popup>
</div>
</template>
<script lang="ts" setup name="brandEdit">
import Popup from '@/components/popup/index.vue'
import type { PropType } from 'vue'
defineProps({
dictData: {
type: Object as PropType<Record<string, any[]>>,
default: () => ({})
}
})
const emit = defineEmits(['success', 'close'])
const popupRef = shallowRef<InstanceType<typeof Popup>>()
const mode = ref('add')
//
const popupTitle = computed(() => {
return '品牌详情'
})
//
const formData = reactive({
id: '',
name: '',
py: '',
code: '',
type: '',
data: '',
more: '',
sort: '',
})
//
const formRules = reactive<any>({
name: [{
required: true,
message: '请输入品牌名称',
trigger: ['blur']
}]
})
//
const setFormData = async (data: Record<any, any>) => {
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
//@ts-ignore
formData[key] = data[key]
}
}
}
//
// const handleSubmit = async () => {
// await formRef.value?.validate()
// const data = { ...formData, }
// mode.value == 'edit'
// ? await apiBrandEdit(data)
// : await apiBrandAdd(data)
// popupRef.value?.close()
// emit('success')
// }
//
const open = (type = 'add') => {
mode.value = type
popupRef.value?.open()
}
//
const handleClose = () => {
emit('close')
}
defineExpose({
open,
setFormData,
})
</script>

View File

@ -0,0 +1,144 @@
<template>
<div class="edit-popup">
<popup ref="popupRef" :title="popupTitle" :async="true" width="550px" @confirm="handleSubmit" @close="handleClose">
<el-form ref="formRef" :model="formData" label-width="90px" :rules="formRules">
<el-form-item label="条码名称" prop="name">
<el-input v-model="formData.name" clearable placeholder="请输入条码名称" />
</el-form-item>
<!-- <el-form-item label="拼音信息" prop="py">
<el-input v-model="formData.py" clearable placeholder="请输入拼音信息" />
</el-form-item> -->
<el-form-item label="条码内容" prop="code">
<el-input v-model="formData.code" clearable placeholder="请输入条码内容" />
</el-form-item>
<el-form-item label="条码类型" prop="type">
<el-select class="flex-1" v-model="formData.type" clearable placeholder="请选择条码类型">
<el-option v-for="(item, index) in dictData.code_type" :key="index" :label="item.name"
:value="parseInt(item.value)" />
</el-select>
</el-form-item>
<el-form-item label="备注信息" prop="data">
<el-input v-model="formData.data" type="textarea" clearable placeholder="请输入备注信息" />
</el-form-item>
<el-form-item label="扩展信息" prop="more">
<el-input v-model="formData.more" type="textarea" clearable placeholder="请输入扩展信息" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="formData.sort" clearable placeholder="请输入排序" />
</el-form-item>
</el-form>
</popup>
</div>
</template>
<script lang="ts" setup name="codeEdit">
import type { FormInstance } from 'element-plus'
import Popup from '@/components/popup/index.vue'
import { apiCodeAdd, apiCodeEdit, apiCodeDetail } from '@/api/code'
import { timeFormat } from '@/utils/util'
import type { PropType } from 'vue'
defineProps({
dictData: {
type: Object as PropType<Record<string, any[]>>,
default: () => ({})
}
})
const emit = defineEmits(['success', 'close'])
const formRef = shallowRef<FormInstance>()
const popupRef = shallowRef<InstanceType<typeof Popup>>()
const mode = ref('add')
//
const popupTitle = computed(() => {
return mode.value == 'edit' ? '编辑条码表' : '新增条码表'
})
//
const formData = reactive({
id: '',
name: '',
py: '',
code: '',
type: '',
data: '',
more: '',
sort: '',
})
//
const formRules = reactive<any>({
name: [{
required: true,
message: '请输入条码名称',
trigger: ['blur']
}],
py: [{
required: true,
message: '请输入拼音信息',
trigger: ['blur']
}],
code: [{
required: true,
message: '请输入条码内容',
trigger: ['blur']
}],
type: [{
required: true,
message: '请选择条码类型0:条形码 | 1:二维码',
trigger: ['blur']
}]
})
//
const setFormData = async (data: Record<any, any>) => {
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
//@ts-ignore
formData[key] = data[key]
}
}
}
const getDetail = async (row: Record<string, any>) => {
const data = await apiCodeDetail({
id: row.id
})
setFormData(data)
}
//
const handleSubmit = async () => {
await formRef.value?.validate()
const data = { ...formData, }
mode.value == 'edit'
? await apiCodeEdit(data)
: await apiCodeAdd(data)
popupRef.value?.close()
emit('success')
}
//
const open = (type = 'add') => {
mode.value = type
popupRef.value?.open()
}
//
const handleClose = () => {
emit('close')
}
defineExpose({
open,
setFormData,
getDetail
})
</script>

View File

@ -0,0 +1,76 @@
<template>
<div>
<el-card class="!border-none mb-4" shadow="never">
<el-form class="mb-[-16px]" :model="queryParams" inline>
<el-form-item label="条码名称" prop="name">
<el-input
class="w-[280px]"
v-model="queryParams.name"
clearable
placeholder="请输入条码名称"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="!border-none" v-loading="pager.loading" shadow="never">
<div class="mt-4">
<el-table :data="pager.lists">
<el-table-column label="ID" prop="id" width="55" />
<el-table-column label="名称" prop="name" show-overflow-tooltip />
<el-table-column label="商品款数" prop="nums" show-overflow-tooltip />
<el-table-column label="状态" prop="status" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.status == 1">已报价</span>
<span v-if="row.status == 0" style="color: #f56c6c">待报价</span>
</template>
</el-table-column>
<el-table-column label="操作" width="170" fixed="right">
<template #default="{ row }">
<el-button link @click="handleDetail(row.id)" type="primary">
<router-link
:to="{
path: 'quoteOffer',
query: {
id: row.id,
date: row.name.substring(0, 10)
},
}"
>
立即报价
</router-link>
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex mt-4 justify-end">
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
</div>
</template>
<script lang="ts" setup name="codeLists">
import { usePaging } from "@/hooks/usePaging";
import { useDictData } from "@/hooks/useDictOptions";
import { opurchaseGoodsOfferDateListsApi } from "@/api/quote";
import feedback from "@/utils/feedback";
//
const queryParams = reactive({});
//
const { pager, getLists, resetParams, resetPage } = usePaging({
fetchFun: opurchaseGoodsOfferDateListsApi,
params: queryParams,
});
getLists();
const handleDetail = (e) => {};
</script>

View File

@ -0,0 +1,244 @@
<template>
<div>
<el-card class="!border-none mb-4" shadow="never">
<el-tabs v-model="activeName" class="demo-tabs" @tab-change="tabChange">
<el-tab-pane label="待报价" name="tobe">
<div class="flex mb-4">
<el-button type="primary" @click="submit">提交报价</el-button>
</div>
<div
class="quote-list"
v-infinite-scroll="getLists(1)"
:infinite-scroll-distance="1000"
:infinite-scroll-delay="500"
:infinite-scroll-immediate="false"
style="overflow: auto"
>
<el-row :gutter="20" style="width: 100%">
<el-col :span="8" v-for="(item, index) in list1" :key="index">
<el-card>
<div class="flex justify-between">
<div>名称: {{ item.goods_name }}</div>
<div>
需求量:
<span style="color: #f56c6c">{{ item.need_num }}</span>
{{ item.unit_name }}
</div>
</div>
<div class="flex justify-between mt-4">
<div class="flex" style="align-items: center">
<span class="mr-2">提供数量: </span>
<el-input
v-model="item.nums"
placeholder="请输入可提供数量"
style="flex: 1"
></el-input>
</div>
<div class="flex" style="align-items: center">
<span class="mr-2">产品报价: </span>
<el-input
v-model="item.price"
placeholder="请输入产品报价"
style="flex: 1"
></el-input>
</div>
</div>
<div class="flex justify-end mt-4">
<div>
{{ item.nums }} {{ item.unit_name }} 合计
<span class="mx-1" style="color: #f56c6c">{{
(item.nums * item.price).toFixed(2)
}}</span>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</el-tab-pane>
<el-tab-pane label="报价记录" name="yet">
<div
class="quote-list not-btn"
v-infinite-scroll="getLists(1)"
:infinite-scroll-distance="1000"
:infinite-scroll-delay="500"
:infinite-scroll-immediate="false"
style="overflow: auto"
>
<el-row :gutter="20" style="width: 100%;">
<el-col :span="8" v-for="(item, index) in list2" :key="index">
<el-card>
<div class="flex justify-between">
<div>名称: {{ item.goods_name }}</div>
<div>
需求量:
<span style="color: #f56c6c">{{ item.need_num }}</span>
{{ item.unit_name }}
</div>
</div>
<div class="flex justify-between mt-4">
<div
class="flex"
style="align-items: center; position: relative"
>
<span class="mr-2">提供数量: </span>
<el-input
v-model="item.nums"
placeholder="请输入可提供数量"
style="flex: 1"
readonly
></el-input>
<div
style="
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 100px;
"
>
<el-image
v-if="item.is_adopt == 1"
src="https://lihai001.oss-cn-chengdu.aliyuncs.com/attach/274ad202405111523222891.png"
></el-image>
<el-image
v-else-if="item.is_adopt == 2"
src="https://lihai001.oss-cn-chengdu.aliyuncs.com/attach/739c3202405071458553459.png"
></el-image>
<el-image
v-else
src="https://lihai001.oss-cn-chengdu.aliyuncs.com/attach/04c2c202405071501462462.png"
></el-image>
</div>
</div>
<div class="flex" style="align-items: center">
<span class="mr-2">产品报价: </span>
<el-input
v-model="item.price"
placeholder="请输入产品报价"
style="flex: 1"
readonly
></el-input>
</div>
</div>
<div class="flex justify-end mt-4">
<div>
{{ item.nums }} {{ item.unit_name }} 合计
<span class="mx-1" style="color: #f56c6c">{{
(item.nums * item.price).toFixed(2)
}}</span>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
<script lang="ts" setup name="quoteOffer">
import { useRoute } from "vue-router";
import {
opurchaseGoodsOfferListsApi,
opurchaseGoodsOfferOfferApi,
} from "@/api/quote";
const route = useRoute();
const form = ref({});
const activeName = ref("tobe");
const activeMap = ref(
new Map([
["detail", true],
["yet", false],
])
);
const tabChange = (type: any) => {
if (type == "yet") {
list2.value = [];
getLists(2);
} else {
list1.value = [];
getLists(1);
}
};
const list1 = ref([]);
const list2 = ref([]);
const queryParams = reactive({
date: route.query.date,
});
const getLists = (e = 1) => {
opurchaseGoodsOfferListsApi({
type: e,
...queryParams,
}).then((res) => {
if (e == 1) {
list1.value = [...list1.value, ...res.lists];
} else {
list2.value = [...list2.value, ...res.lists];
}
});
};
getLists();
const submit = () => {
let data = list1.value
.filter((item) => {
return +item.price && +item.nums;
})
.map((item: any) => {
return {
id: item.id,
nums: item.nums,
price: item.price,
};
});
if (!data.length) return ElMessage.error("请先填写产品报价");
opurchaseGoodsOfferOfferApi({
data: data,
}).then((res) => {
getLists();
});
};
</script>
<style scoped lang="scss">
.quote-list {
height: calc(100vh - 300px);
width: 100%;
}
.not-btn{
height: calc(100vh - 252px);
}
/* 修改滚动条的样式 */
::-webkit-scrollbar {
width: 0.315rem; /* 设置滚动条的宽度 */
}
/* 设置滚动条的轨道样式 */
::-webkit-scrollbar-track {
background-color: #f1f1f1; /* 设置轨道的背景色 */
margin: 1.25rem 0;
}
/* 设置滚动条的滑块样式 */
::-webkit-scrollbar-thumb {
background-color: #ccc; /* 设置滑块的背景色 */
border-radius: 0.315rem; /* 设置滑块的圆角 */
}
/* 设置滚动条鼠标悬停时的滑块样式 */
::-webkit-scrollbar-thumb:hover {
background-color: #999; /* 设置鼠标悬停时滑块的背景色 */
}
</style>

View File

@ -46,7 +46,7 @@
</template> </template>
提交今日商户采购订单 提交今日商户采购订单
</el-button> </el-button>
<el-button <!-- <el-button
v-perms="['operation.opurchaseclass/add']" v-perms="['operation.opurchaseclass/add']"
type="success" type="success"
@click="onPrintOrder" @click="onPrintOrder"
@ -55,10 +55,10 @@
<icon name="el-icon-Printer" /> <icon name="el-icon-Printer" />
</template> </template>
打印 打印
</el-button> </el-button> -->
<div class="mt-4"> <div class="mt-4">
<el-table :data="pager.lists" @selection-change="handleSelectionChange"> <el-table :data="pager.lists" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" /> <!-- <el-table-column type="selection" width="55" /> -->
<el-table-column label="ID" prop="id" show-overflow-tooltip /> <el-table-column label="ID" prop="id" show-overflow-tooltip />
<el-table-column <el-table-column
label="所属商户" label="所属商户"
@ -114,6 +114,9 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" width="120" fixed="right"> <el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<!-- <el-button type="primary" link @click="onPrintOrder(row)">
打印
</el-button> -->
<el-button type="primary" link @click="handleDetail(row)"> <el-button type="primary" link @click="handleDetail(row)">
详情 详情
</el-button> </el-button>
@ -166,10 +169,12 @@ import { useRouter } from "vue-router";
import { print, testPrint } from "@/utils/print"; import { print, testPrint } from "@/utils/print";
// , , 使 // , , 使
const onPrintOrder = () => { const onPrintOrder = (row: any) => {
testPrint({ console.log(row);
name: 'test'
}) // testPrint({
// name: 'test'
// })
}; };
const router = useRouter(); const router = useRouter();

View File

@ -32,7 +32,7 @@
<el-form-item label="名称" prop="name"> <el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" clearable /> <el-input v-model="formData.name" placeholder="请输入名称" clearable />
</el-form-item> </el-form-item>
<el-form-item label="归属部门" prop="dept_id"> <!-- <el-form-item label="归属部门" prop="dept_id">
<el-tree-select <el-tree-select
class="flex-1" class="flex-1"
v-model="formData.dept_id" v-model="formData.dept_id"
@ -51,7 +51,7 @@
:default-expand-all="true" :default-expand-all="true"
placeholder="请选择上级部门" placeholder="请选择上级部门"
/> />
</el-form-item> </el-form-item> -->
<el-form-item label="岗位" prop="jobs_id"> <el-form-item label="岗位" prop="jobs_id">
<el-select <el-select
class="flex-1" class="flex-1"

View File

@ -38,7 +38,7 @@
</el-button> --> </el-button> -->
<div class="mt-4"> <div class="mt-4">
<el-table :data="pager.lists" @selection-change="handleSelectionChange"> <el-table :data="pager.lists" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" /> <!-- <el-table-column type="selection" width="55" /> -->
<el-table-column label="ID" prop="id" show-overflow-tooltip /> <el-table-column label="ID" prop="id" show-overflow-tooltip />
<el-table-column <el-table-column
label="所属商户" label="所属商户"