From 0181a84b01b1960931b712d5d7b7bdabe8bb1d2c Mon Sep 17 00:00:00 2001
From: weipengfei <2187978347@qq.com>
Date: Sat, 25 May 2024 15:15:08 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .env.development                             |   8 +-
 src/api/quote.ts                             |  23 ++
 src/enums/pageEnum.ts                        |   1 +
 src/enums/requestEnums.ts                    |   1 +
 src/layout/default/index.vue                 |  55 +++-
 src/permission.ts                            |  16 +-
 src/router/routes.ts                         |   4 +
 src/stores/modules/user.ts                   |   9 +-
 src/utils/print.ts                           | 291 ++++++++++++++-----
 src/utils/request/index.ts                   | 236 ++++++++-------
 src/views/account/admin_login.vue            | 131 +++++++++
 src/views/account/login.vue                  |   3 +-
 src/views/data/merchant/audit/index.vue      |   2 +-
 src/views/data/supplier/audit/index.vue      |   2 +-
 src/views/data/supplier/supplier/index.vue   | 157 +++++++---
 src/views/goods/quote/detail.vue             | 111 +++++++
 src/views/goods/quote/edit.vue               | 144 +++++++++
 src/views/goods/quote/index.vue              |  76 +++++
 src/views/goods/quote/quoteOffer.vue         | 244 ++++++++++++++++
 src/views/opurchase/opurchaseclass/index.vue |  19 +-
 src/views/permission/admin/edit.vue          |   4 +-
 src/views/translationOrder/index.vue         |   2 +-
 22 files changed, 1288 insertions(+), 251 deletions(-)
 create mode 100644 src/api/quote.ts
 create mode 100644 src/views/account/admin_login.vue
 create mode 100644 src/views/goods/quote/detail.vue
 create mode 100644 src/views/goods/quote/edit.vue
 create mode 100644 src/views/goods/quote/index.vue
 create mode 100644 src/views/goods/quote/quoteOffer.vue

diff --git a/.env.development b/.env.development
index 682dfda..cde91fe 100644
--- a/.env.development
+++ b/.env.development
@@ -3,8 +3,8 @@ VITE_NOW_TYPE = 'dist'
 # Base API
 # VITE_APP_BASE_URL='http://192.168.1.13:8546'
 
-# VITE_PUSH_URL = 'ws://192.168.1.22:8787'
-# VITE_APP_BASE_URL = 'http://192.168.1.22:8546'
+VITE_PUSH_URL = 'ws://192.168.1.22:8787'
+VITE_APP_BASE_URL = 'http://192.168.1.22:8546'
 
-VITE_PUSH_URL ='wss://erp.lihaink.cn/pull'
-VITE_APP_BASE_URL='https://erp.lihaink.cn'
+# VITE_PUSH_URL ='wss://erp.lihaink.cn/pull'
+# VITE_APP_BASE_URL='https://erp.lihaink.cn'
diff --git a/src/api/quote.ts b/src/api/quote.ts
new file mode 100644
index 0000000..9e6e26c
--- /dev/null
+++ b/src/api/quote.ts
@@ -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 });
+
+}
diff --git a/src/enums/pageEnum.ts b/src/enums/pageEnum.ts
index 1509ff5..f51a3b2 100644
--- a/src/enums/pageEnum.ts
+++ b/src/enums/pageEnum.ts
@@ -1,6 +1,7 @@
 export enum PageEnum {
     //登录页面
     LOGIN = '/login',
+    ADMIN_LOGIN = '/admin_login',
     //无权限页面
     ERROR_403 = '/403',
     INDEX = '/'
diff --git a/src/enums/requestEnums.ts b/src/enums/requestEnums.ts
index 7ee2107..698a7e0 100644
--- a/src/enums/requestEnums.ts
+++ b/src/enums/requestEnums.ts
@@ -13,6 +13,7 @@ export enum RequestMethodsEnum {
 export enum RequestCodeEnum {
     SUCCESS = 1,
     FAIL = 0,
+    SERVER_ERROR = 500,
     LOGIN_FAILURE = -1,
     OPEN_NEW_PAGE = 2
 }
diff --git a/src/layout/default/index.vue b/src/layout/default/index.vue
index c0fbf03..8dfa0a6 100644
--- a/src/layout/default/index.vue
+++ b/src/layout/default/index.vue
@@ -20,6 +20,7 @@ import LayoutMain from './components/main.vue'
 import LayoutSidebar from './components/sidebar/index.vue'
 import LayoutHeader from './components/header/index.vue'
 import { Push } from "@/common/push.js";
+import { print } from "@/utils/print";
 
 const connection = new Push({
   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(`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-2频道有message事件的消息时
-user_channel.on('message', function (data) {
+user_channel.on('message', function (data: any) {
   console.log("收到消息--",data);
   if(data.event=='platform_print'){
     console.log('收到打印消息');
+    if(typeof data.data != 'object' || !data.data?.info?.length) return ElMessage.error('收到订单,但打印数据不合法,请联系管理员');
+    else print(data.data);
   }
 });
 // 断线事件
diff --git a/src/permission.ts b/src/permission.ts
index decf89b..f7101c4 100644
--- a/src/permission.ts
+++ b/src/permission.ts
@@ -12,20 +12,24 @@ import { PageEnum } from './enums/pageEnum'
 import useTabsStore from './stores/modules/multipleTabs'
 import { clearAuthInfo } from './utils/auth'
 import config from './config'
+import cache from '@/utils/cache'
 
 // NProgress配置
 NProgress.configure({ showSpinner: false })
 
 const loginPath = PageEnum.LOGIN
+const adminLoginPath = PageEnum.ADMIN_LOGIN
 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) => {
     // 开始 Progress Bar
     NProgress.start()
     document.title = to.meta.title ?? config.title
     const userStore = useUserStore()
     const tabsStore = useTabsStore()
+    const is_admin = cache.get('is_admin')
+    
     if (whiteList.includes(to.path)) {
         // 在免登录白名单,直接进入
         next()
@@ -33,7 +37,7 @@ router.beforeEach(async (to, from, next) => {
         // 获取用户信息
         const hasGetUserInfo = Object.keys(userStore.userInfo).length !== 0
         if (hasGetUserInfo) {
-            if (to.path === loginPath) {
+            if (to.path === loginPath || to.path === adminLoginPath) {
                 next({ path: defaultPath })
             } else {
                 next()
@@ -70,11 +74,15 @@ router.beforeEach(async (to, from, next) => {
                 next({ ...to, replace: true })
             } catch (err) {
                 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 {
-        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 } })
     }
 })
 
diff --git a/src/router/routes.ts b/src/router/routes.ts
index a3603cb..f727d0e 100644
--- a/src/router/routes.ts
+++ b/src/router/routes.ts
@@ -34,6 +34,10 @@ export const constantRoutes: Array<RouteRecordRaw> = [
         path: PageEnum.LOGIN,
         component: () => import('@/views/account/login.vue')
     },
+    {
+        path: PageEnum.ADMIN_LOGIN,
+        component: () => import('@/views/account/admin_login.vue')
+    },
     {
         path: '/user',
         component: LAYOUT,
diff --git a/src/stores/modules/user.ts b/src/stores/modules/user.ts
index 27b4ebc..d10e5e2 100644
--- a/src/stores/modules/user.ts
+++ b/src/stores/modules/user.ts
@@ -32,15 +32,17 @@ const useUserStore = defineStore({
             this.perms = []
         },
         login(playload: any) {
-            const { account, password } = playload
+            const { account, password, is_admin } = playload
             return new Promise((resolve, reject) => {
                 login({
                     account: account.trim(),
-                    password: password
+                    password: password,
+                    is_admin: is_admin
                 })
                     .then((data) => {
                         this.token = data.token
                         cache.set(TOKEN_KEY, data.token)
+                        cache.set('is_admin', is_admin)
                         resolve(data)
                     })
                     .catch((error) => {
@@ -53,7 +55,8 @@ const useUserStore = defineStore({
                 logout()
                     .then(async (data) => {
                         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()
                         resolve(data)
                     })
diff --git a/src/utils/print.ts b/src/utils/print.ts
index 550ca85..51f2b40 100644
--- a/src/utils/print.ts
+++ b/src/utils/print.ts
@@ -16,50 +16,188 @@ export const print = (data: any) => {
   const list = hiprint.hiwebSocket.getPrinterList();
   console.log(list);
 
+  let nowHeight = 0;
+  let textHeight = 10;
+
   // 下列方法都是没有拖拽设计页面的, 相当于代码模式, 使用代码设计页面
   // 想要实现拖拽设计页面,请往下看 '自定义设计'
   var hiprintTemplate = new hiprint.PrintTemplate();
 
+  // 纸张高度 固定为 130 pt + 商品数量高度
+  let oneHeight = 120 + Math.ceil((data.info.length * 2 * 10) / 2.84);
+
   // 模板宽度单位是mm ( 1mm ~= 2.84pt ) 其他的宽高单位是pt
   var panel = hiprintTemplate.addPrintPanel({
-    width: WIDTH, // 58mm = 164pt
-    height: 58,
+    width: 58, // 58mm = 164pt
+    height: oneHeight,
     paperNumberDisabled: true,
-    topOffset: -1,
   });
 
-  //文本
-  panel.addPrintText({
-    options: {
+  let options = (e: any) => {
+    nowHeight += textHeight;
+    let opt = {
       width: T_WIDTH,
-      height: 10,
-      top: 10,
+      height: textHeight,
+      top: nowHeight,
       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",
-    },
+    })
+  );
+
+  //文本 *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: {
       width: T_WIDTH,
-      height: 35,
-      top: 30,
+      height: 50,
+      top: nowHeight + 15,
       left: T_LEFT,
-      title: "长文本:hiprint是一个很好的webjs打印,浏览器在的地方他都可以运行",
+      title: "",
+      src: "https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png",
     },
   });
-  //长文本
-  panel.addPrintLongText({
+  nowHeight += 60;
+  // 文本 *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: {
       width: T_WIDTH,
-      height: 35,
-      top: 80,
+      height: 50,
+      top: nowHeight + 15,
       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({});
   //直接打印,需要安装客户端
@@ -94,25 +232,25 @@ export const testPrint = () => {
     paperNumberDisabled: true,
   });
 
-  let options = (e: any)=>{
-    nowHeight+=textHeight;
+  let options = (e: any) => {
+    nowHeight += textHeight;
     let opt = {
       width: T_WIDTH,
       height: textHeight,
       top: nowHeight,
       left: T_LEFT,
-      title: '',
-    }
-    if(typeof e === 'string') opt.title = e;
+      title: "",
+    };
+    if (typeof e === "string") opt.title = e;
     else opt = Object.assign(opt, e);
     return {
-      options: opt
-    }
-  }
+      options: opt,
+    };
+  };
 
-  let lineTitle = ()=>{
-    nowHeight+=textHeight;
-    let left = Math.floor(T_WIDTH/3) ;
+  let lineTitle = () => {
+    nowHeight += textHeight;
+    let left = Math.floor(T_WIDTH / 3);
     panel.addPrintText({
       options: {
         width: left,
@@ -143,18 +281,18 @@ export const testPrint = () => {
         textAlign: "center",
       },
     });
-  }
+  };
 
-  let lineOptions = (text1: any, text2: any, text3: any)=>{
-    nowHeight+=textHeight;
-    let left = Math.floor(T_WIDTH/3) ;
+  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+"",
+        title: text1 + "",
         textAlign: "center",
       },
     });
@@ -164,7 +302,7 @@ export const testPrint = () => {
         height: textHeight,
         top: nowHeight,
         left: left,
-        title: text2+"",
+        title: text2 + "",
         textAlign: "center",
       },
     });
@@ -174,17 +312,19 @@ export const testPrint = () => {
         height: textHeight,
         top: nowHeight,
         left: left * 2,
-        title: text3+"",
+        title: text3 + "",
         textAlign: "center",
       },
     });
-  }
+  };
 
   //文本 *1
-  panel.addPrintText(options({
-    title: '泸优采-小票0',
-    textAlign: "center",
-  }));
+  panel.addPrintText(
+    options({
+      title: "泸优采-小票0",
+      textAlign: "center",
+    })
+  );
 
   //文本 *6
   panel.addPrintText(options("单号: PF171617436315965155"));
@@ -205,11 +345,11 @@ export const testPrint = () => {
 
   //文本 *13
   panel.addPrintText(options("======================"));
-  panel.addPrintText(options('合计: 0.75元'));
-  
-  panel.addPrintText(options('提货点: 莲花农贸市场'));
-  panel.addPrintText(options('提货点电话: 19330904744'));
-  panel.addPrintText(options('提货点负责人签字:'));
+  panel.addPrintText(options("合计: 0.75元"));
+
+  panel.addPrintText(options("提货点: 莲花农贸市场"));
+  panel.addPrintText(options("提货点电话: 19330904744"));
+  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 } });
@@ -217,38 +357,37 @@ export const testPrint = () => {
   panel.addPrintImage({
     options: {
       width: T_WIDTH,
-      height:50,
-      top: nowHeight + 15, 
-      left: T_LEFT, 
-      title: '',
-      src: 'https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png'
-    }
-  })
-  nowHeight+=60;
-  panel.addPrintText(options('收货人: 阿哈'));
-  panel.addPrintText(options('收货地址: 里海科技'));
-  panel.addPrintText(options('联系电话: 17685151643'));
-  panel.addPrintText(options('收货人签字:'));
+      height: 50,
+      top: nowHeight + 15,
+      left: T_LEFT,
+      title: "",
+      src: "https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png",
+    },
+  });
+  nowHeight += 60;
+  panel.addPrintText(options("收货人: 阿哈"));
+  panel.addPrintText(options("收货地址: 里海科技"));
+  panel.addPrintText(options("联系电话: 17685151643"));
+  panel.addPrintText(options("收货人签字:"));
   // panel.addPrintRect({ options: { width: T_WIDTH, height:30,top: nowHeight+15, left: T_LEFT,borderColor:'',borderWidth:0.75 } });
   panel.addPrintImage({
     options: {
       width: T_WIDTH,
       height: 50,
-      top: nowHeight + 15, 
-      left: T_LEFT, 
-      title: '',
-      src: 'https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png'
-    }
-  })
-  nowHeight+=60;
+      top: nowHeight + 15,
+      left: T_LEFT,
+      title: "",
+      src: "https://lihai001.oss-cn-chengdu.aliyuncs.com/def/db264202405221455038529.png",
+    },
+  });
+  nowHeight += 60;
 
   panel.addPrintText(options(""));
   panel.addPrintText(options(""));
   panel.addPrintText(options(""));
   panel.addPrintText(options("======================"));
 
-  // 合计高度 23 + length * 2 
-
+  // 合计高度 23 + length * 2
 
   //打印
   // hiprintTemplate.print({});
@@ -258,22 +397,24 @@ export const testPrint = () => {
   // 发送任务到打印机成功
   hiprintTemplate.on("printSuccess", (e: any) => {
     console.log("printSuccess", e);
+    ElMessage.success('订单已加入打印队列');
   });
   // 发送任务到打印机失败
   hiprintTemplate.on("printError", (e: any) => {
     console.log("printError", e);
+    ElMessage.error('打印失败,请检查是否正确连接打印机!');
   });
 };
 
-export const printerList = ()=>{
-  try{
+export const printerList = () => {
+  try {
     const list = hiprint.hiwebSocket.getPrinterList();
     return list;
-  }catch{
-    ElMessage.error("请先安装打印机客户端")
+  } catch {
+    ElMessage.error("请先安装打印机客户端");
     return [];
   }
-}
+};
 
 // 计算字符串长度
 function calculateStringLength(str: string) {
diff --git a/src/utils/request/index.ts b/src/utils/request/index.ts
index e66915d..6c5f649 100644
--- a/src/utils/request/index.ts
+++ b/src/utils/request/index.ts
@@ -1,123 +1,133 @@
-import { merge } from 'lodash'
-import configs from '@/config'
-import { Axios } from './axios'
-import { ContentTypeEnum, RequestCodeEnum, RequestMethodsEnum } from '@/enums/requestEnums'
-import type { AxiosHooks } from './type'
-import { clearAuthInfo, getToken } from '../auth'
-import feedback from '../feedback'
-import NProgress from 'nprogress'
-import { AxiosError, type AxiosRequestConfig } from 'axios'
-import router from '@/router'
-import { PageEnum } from '@/enums/pageEnum'
+import { merge } from "lodash";
+import configs from "@/config";
+import { Axios } from "./axios";
+import {
+  ContentTypeEnum,
+  RequestCodeEnum,
+  RequestMethodsEnum,
+} from "@/enums/requestEnums";
+import type { AxiosHooks } from "./type";
+import { clearAuthInfo, getToken } from "../auth";
+import feedback from "../feedback";
+import NProgress from "nprogress";
+import { AxiosError, type AxiosRequestConfig } from "axios";
+import router from "@/router";
+import { PageEnum } from "@/enums/pageEnum";
 
 // 处理axios的钩子函数
 const axiosHooks: AxiosHooks = {
-    requestInterceptorsHook(config) {
-        NProgress.start()
-        const { withToken, isParamsToData } = config.requestOptions
-        const params = config.params || {}
-        const headers = config.headers || {}
+  requestInterceptorsHook(config) {
+    NProgress.start();
+    const { withToken, isParamsToData } = config.requestOptions;
+    const params = config.params || {};
+    const headers = config.headers || {};
 
-        // 添加token
-        if (withToken) {
-            const token = getToken()
-            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)
+    // 添加token
+    if (withToken) {
+      const token = getToken();
+      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.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 = {
-    //接口超时时间
-    timeout: configs.timeout,
-    // 基础接口地址
-    baseURL: configs.baseUrl,
-    //请求头
-    headers: { 'Content-Type': ContentTypeEnum.JSON, version: configs.version },
-    // 处理 axios的钩子函数
-    axiosHooks: axiosHooks,
-    // 每个接口可以单独配置
-    requestOptions: {
-        // 是否将params视为data参数,仅限post请求
-        isParamsToData: true,
-        //是否返回默认的响应
-        isReturnDefaultResponse: false,
-        // 需要对返回数据进行处理
-        isTransformResponse: true,
-        // 接口拼接地址
-        urlPrefix: configs.urlPrefix,
-        // 忽略重复请求
-        ignoreCancelToken: false,
-        // 是否携带token
-        withToken: true,
-        // 开启请求超时重新发起请求请求机制
-        isOpenRetry: true,
-        // 重新请求次数
-        retryCount: 2
-    }
-}
+  //接口超时时间
+  timeout: configs.timeout,
+  // 基础接口地址
+  baseURL: configs.baseUrl,
+  //请求头
+  headers: { "Content-Type": ContentTypeEnum.JSON, version: configs.version },
+  // 处理 axios的钩子函数
+  axiosHooks: axiosHooks,
+  // 每个接口可以单独配置
+  requestOptions: {
+    // 是否将params视为data参数,仅限post请求
+    isParamsToData: true,
+    //是否返回默认的响应
+    isReturnDefaultResponse: false,
+    // 需要对返回数据进行处理
+    isTransformResponse: true,
+    // 接口拼接地址
+    urlPrefix: configs.urlPrefix,
+    // 忽略重复请求
+    ignoreCancelToken: false,
+    // 是否携带token
+    withToken: true,
+    // 开启请求超时重新发起请求请求机制
+    isOpenRetry: true,
+    // 重新请求次数
+    retryCount: 2,
+  },
+};
 
 function createAxios(opt?: Partial<AxiosRequestConfig>) {
-    return new Axios(
-        // 深度合并
-        merge(defaultOptions, opt || {})
-    )
+  return new Axios(
+    // 深度合并
+    merge(defaultOptions, opt || {})
+  );
 }
-const request = createAxios()
-export default request
+const request = createAxios();
+export default request;
diff --git a/src/views/account/admin_login.vue b/src/views/account/admin_login.vue
new file mode 100644
index 0000000..5c33fba
--- /dev/null
+++ b/src/views/account/admin_login.vue
@@ -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>
diff --git a/src/views/account/login.vue b/src/views/account/login.vue
index 74b1aeb..ddc1837 100644
--- a/src/views/account/login.vue
+++ b/src/views/account/login.vue
@@ -68,7 +68,8 @@ const remAccount = ref(false)
 const config = computed(() => appStore.config)
 const formData = reactive({
     account: '',
-    password: ''
+    password: '',
+    is_admin: 0  //1管理员, 0是供应链
 })
 const rules = {
     account: [
diff --git a/src/views/data/merchant/audit/index.vue b/src/views/data/merchant/audit/index.vue
index 31c7f8e..22dce02 100644
--- a/src/views/data/merchant/audit/index.vue
+++ b/src/views/data/merchant/audit/index.vue
@@ -281,7 +281,7 @@
                 审核
               </el-button>
               <el-button
-                v-else-if="!row.status"
+                v-else-if="!row.status && !row.mark"
                 link
                 type="warning"
                 @click="handleDetail(row)"
diff --git a/src/views/data/supplier/audit/index.vue b/src/views/data/supplier/audit/index.vue
index 3d8925a..ad92872 100644
--- a/src/views/data/supplier/audit/index.vue
+++ b/src/views/data/supplier/audit/index.vue
@@ -56,7 +56,7 @@
                             <el-button v-if="!row.status && (row.mark=='' || row.mark==null)" link type="primary" @click="handleDetail(row)">
                                 审核
                             </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>
                         </template>
diff --git a/src/views/data/supplier/supplier/index.vue b/src/views/data/supplier/supplier/index.vue
index 884c62e..40363ea 100644
--- a/src/views/data/supplier/supplier/index.vue
+++ b/src/views/data/supplier/supplier/index.vue
@@ -5,23 +5,43 @@
         <el-row>
           <el-col :span="6">
             <el-form-item label="供应商分类" prop="category_id">
-              <el-select v-model="queryParams.category_id" clearable placeholder="请选择供应商类型">
-                <el-option v-for="(item, index) in dictData.mer_category_type" :key="index" :label="item.name"
-                  :value="parseInt(item.value)" />
+              <el-select
+                v-model="queryParams.category_id"
+                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-form-item>
           </el-col>
           <el-col :span="6">
             <el-form-item label="供应商类型" prop="type_id">
-              <el-select v-model="queryParams.type_id" clearable placeholder="请选择供应商类型">
-                <el-option v-for="(item, index) in dictData.merchat_type" :key="index" :label="item.name"
-                  :value="parseInt(item.value)" />
+              <el-select
+                v-model="queryParams.type_id"
+                clearable
+                placeholder="请选择供应商类型"
+              >
+                <el-option
+                  v-for="(item, index) in dictData.merchat_type"
+                  :key="index"
+                  :label="item.name"
+                  :value="parseInt(item.value)"
+                />
               </el-select>
             </el-form-item>
           </el-col>
           <el-col :span="6">
             <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-col>
           <el-col :span="6">
@@ -34,13 +54,21 @@
       </el-form>
     </el-card>
     <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>
           <icon name="el-icon-Plus" />
         </template>
         新增
       </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>
       <div class="mt-4">
@@ -59,14 +87,33 @@
               />
             </template>
           </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 }">
-              <dict-value :options="dictData.merchat_type" :value="row.type_id" />
+              <dict-value
+                :options="dictData.merchat_type"
+                :value="row.type_id"
+              />
             </template>
           </el-table-column>
-          <el-table-column label="供应商名称" 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
+            label="供应商名称"
+            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
             label="标签"
             prop="sys_labels_arr"
@@ -78,14 +125,26 @@
               }}</span>
             </template>
           </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">
             <template #default="{ row }">
               <dict-value :options="dictData.show_status" :value="row.status" />
             </template>
           </el-table-column>
-          <el-table-column label="提成比例" prop="commission_rate" show-overflow-tooltip />
-          <el-table-column label="供应商手续费单独设置" width="200" prop="commission_switch">
+          <el-table-column
+            label="提成比例"
+            prop="commission_rate"
+            show-overflow-tooltip
+          />
+          <el-table-column
+            label="供应商手续费单独设置"
+            width="200"
+            prop="commission_switch"
+          >
             <template #default="{ row }">
               <!-- <dict-value :options="dictData.show_status" :value="row.commission_switch" /> -->
               {{ row.commission_switch ? "开启" : "关闭" }}
@@ -98,22 +157,42 @@
           />
           <el-table-column label="操作" width="200" fixed="right">
             <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 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 link @click="handleDetail(row.id)"> 详情 </el-button>
-              <router-link :to="{
-                path: 'bindGoods',
-                query: {
-                  id: row.id,
-                },
-              }">
-                <el-button link type="primary"> 商品绑定 </el-button>
-              </router-link>
-              <el-button v-if="!row.uid" link @click="bindUser(row)" type="primary">
+
+              <el-button link type="primary">
+                <router-link
+                  :to="{
+                    path: 'bindGoods',
+                    query: {
+                      id: row.id,
+                    },
+                  }"
+                >
+                  商品绑定
+                </router-link>
+              </el-button>
+              <el-button
+                v-if="!row.uid"
+                link
+                @click="bindUser(row)"
+                type="primary"
+              >
                 绑定用户
               </el-button>
               <el-button link @click="setBalance(row.id)"> 设置余额 </el-button>
@@ -151,15 +230,17 @@
           </el-radio-group>
         </el-form-item>
         <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>
       <template #footer>
         <div class="dialog-footer">
           <el-button @click="showBalance = false">取消</el-button>
-          <el-button type="primary" @click="submitBalance">
-            确认
-          </el-button>
+          <el-button type="primary" @click="submitBalance"> 确认 </el-button>
         </div>
       </template>
     </el-dialog>
@@ -174,7 +255,7 @@ import {
   apiSupplierDelete,
   apiSupplierDetail,
   apiSupplierSetBalance,
-  apiSupplierBindUser
+  apiSupplierBindUser,
 } from "@/api/supplier";
 import { timeFormat } from "@/utils/util";
 import feedback from "@/utils/feedback";
@@ -246,7 +327,7 @@ const handleDetail = async (id: any) => {
 
 const showDialog = ref(false);
 const bindData = ref({
-  id: ""
+  id: "",
 });
 // 绑定用户
 const bindUser = (data: any) => {
@@ -261,7 +342,6 @@ const onBind = (data: any) => {
     showDialog.value = false;
     getLists();
   });
-  
 };
 
 const balanceInfo = ref({
@@ -277,8 +357,9 @@ const setBalance = (id: any) => {
   balanceInfo.value.type = 1;
   showBalance.value = true;
 };
-const submitBalance = ()=>{
-  if(+balanceInfo.value.set_money <=0 || balanceInfo.value.set_money == '') return ElMessage.error('请输入正确的金额');
+const submitBalance = () => {
+  if (+balanceInfo.value.set_money <= 0 || balanceInfo.value.set_money == "")
+    return ElMessage.error("请输入正确的金额");
   apiSupplierSetBalance({
     set_money: +balanceInfo.value.set_money,
     id: balanceInfo.value.id,
@@ -287,7 +368,7 @@ const submitBalance = ()=>{
     showBalance.value = false;
     getLists();
   });
-}
+};
 
 getLists();
 </script>
diff --git a/src/views/goods/quote/detail.vue b/src/views/goods/quote/detail.vue
new file mode 100644
index 0000000..ae25e2e
--- /dev/null
+++ b/src/views/goods/quote/detail.vue
@@ -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>
diff --git a/src/views/goods/quote/edit.vue b/src/views/goods/quote/edit.vue
new file mode 100644
index 0000000..888a2f0
--- /dev/null
+++ b/src/views/goods/quote/edit.vue
@@ -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>
diff --git a/src/views/goods/quote/index.vue b/src/views/goods/quote/index.vue
new file mode 100644
index 0000000..e9d30e2
--- /dev/null
+++ b/src/views/goods/quote/index.vue
@@ -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>
diff --git a/src/views/goods/quote/quoteOffer.vue b/src/views/goods/quote/quoteOffer.vue
new file mode 100644
index 0000000..134a84b
--- /dev/null
+++ b/src/views/goods/quote/quoteOffer.vue
@@ -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>
diff --git a/src/views/opurchase/opurchaseclass/index.vue b/src/views/opurchase/opurchaseclass/index.vue
index 730a106..102a4c8 100644
--- a/src/views/opurchase/opurchaseclass/index.vue
+++ b/src/views/opurchase/opurchaseclass/index.vue
@@ -46,7 +46,7 @@
         </template>
         提交今日商户采购订单
       </el-button>
-      <el-button
+      <!-- <el-button
         v-perms="['operation.opurchaseclass/add']"
         type="success"
         @click="onPrintOrder"
@@ -55,10 +55,10 @@
           <icon name="el-icon-Printer" />
         </template>
         打印
-      </el-button>
+      </el-button> -->
       <div class="mt-4">
         <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="所属商户"
@@ -114,6 +114,9 @@
           </el-table-column>
           <el-table-column label="操作" width="120" fixed="right">
             <template #default="{ row }">
+              <!-- <el-button type="primary" link @click="onPrintOrder(row)">
+                打印
+              </el-button> -->
               <el-button type="primary" link @click="handleDetail(row)">
                 详情
               </el-button>
@@ -166,10 +169,12 @@ import { useRouter } from "vue-router";
 import { print, testPrint } from "@/utils/print";
 
 // 下列方法都是没有拖拽设计页面的, 相当于代码模式, 使用代码设计页面
-const onPrintOrder = () => {
-  testPrint({
-    name: 'test'
-  })
+const onPrintOrder = (row: any) => {
+  console.log(row);
+  
+  // testPrint({
+  //   name: 'test'
+  // })
 };
 
 const router = useRouter();
diff --git a/src/views/permission/admin/edit.vue b/src/views/permission/admin/edit.vue
index decf6a5..12163c7 100644
--- a/src/views/permission/admin/edit.vue
+++ b/src/views/permission/admin/edit.vue
@@ -32,7 +32,7 @@
                 <el-form-item label="名称" prop="name">
                     <el-input v-model="formData.name" placeholder="请输入名称" clearable />
                 </el-form-item>
-                <el-form-item label="归属部门" prop="dept_id">
+                <!-- <el-form-item label="归属部门" prop="dept_id">
                     <el-tree-select
                         class="flex-1"
                         v-model="formData.dept_id"
@@ -51,7 +51,7 @@
                         :default-expand-all="true"
                         placeholder="请选择上级部门"
                     />
-                </el-form-item>
+                </el-form-item> -->
                 <el-form-item label="岗位" prop="jobs_id">
                     <el-select
                         class="flex-1"
diff --git a/src/views/translationOrder/index.vue b/src/views/translationOrder/index.vue
index d1dc20b..964629d 100644
--- a/src/views/translationOrder/index.vue
+++ b/src/views/translationOrder/index.vue
@@ -38,7 +38,7 @@
             </el-button> -->
       <div class="mt-4">
         <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="所属商户"