From 64c2870483568cdeb0206661249a19c5b06de8fe Mon Sep 17 00:00:00 2001
From: zhuguifei <312353457@qq.com>
Date: 星期三, 04 三月 2026 12:52:52 +0800
Subject: [PATCH] feat(moudule(qa)): 新增喂丝机对应关系、第三方储丝柜信息视图等管理界面

---
 ruoyi-plus-soybean/src/router/elegant/routes.ts                                     |   38 
 ruoyi-plus-soybean/src/views/analy/store-silk/index.vue                             |  273 +++++++
 ruoyi-plus-soybean/src/typings/api/md.shift.api.d.ts                                |   77 +
 ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-search.vue         |  101 ++
 ruoyi-plus-soybean/src/views/md/shift/index.vue                                     |  254 ++++++
 ruoyi-plus-soybean/src/service/api/analy/feed-match.ts                              |   35 
 ruoyi-plus-soybean/src/views/md/shift/modules/shift-operate-drawer.vue              |  175 ++++
 ruoyi-plus-soybean/src/views/md/shift/modules/shift-search.vue                      |   77 +
 ruoyi-plus-soybean/src/views/analy/feed-match/modules/feed-match-search.vue         |   79 ++
 ruoyi-plus-soybean/src/typings/api/analy.feed-match.api.d.ts                        |  118 +++
 ruoyi-plus-soybean/src/views/analy/feed-match/modules/feed-match-operate-drawer.vue |  281 +++++++
 ruoyi-plus-soybean/src/router/elegant/imports.ts                                    |    3 
 ruoyi-plus-soybean/src/typings/elegant-router.d.ts                                  |    8 
 ruoyi-plus-soybean/src/typings/components.d.ts                                      |    4 
 ruoyi-plus-soybean/src/views/analy/feed-match/index.vue                             |  337 ++++++++
 ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-operate-drawer.vue |  176 ++++
 RuoYi-Vue-Plus/开发日志.md                                                              |  113 --
 ruoyi-plus-soybean/src/service/api/md/shift.ts                                      |   35 
 ruoyi-plus-soybean/src/typings/api/analy.store-silk.api.d.ts                        |   74 +
 ruoyi-plus-soybean/src/service/api/analy/store-silk.ts                              |   35 
 ruoyi-plus-soybean/src/router/elegant/transform.ts                                  |    4 
 21 files changed, 2,189 insertions(+), 108 deletions(-)

diff --git "a/RuoYi-Vue-Plus/\345\274\200\345\217\221\346\227\245\345\277\227.md" "b/RuoYi-Vue-Plus/\345\274\200\345\217\221\346\227\245\345\277\227.md"
index 3e10167..002cc10 100644
--- "a/RuoYi-Vue-Plus/\345\274\200\345\217\221\346\227\245\345\277\227.md"
+++ "b/RuoYi-Vue-Plus/\345\274\200\345\217\221\346\227\245\345\277\227.md"
@@ -8,112 +8,9 @@
 - 1.鏂板鎴愬搧妫� 妫�楠屾壒娆� 绠$悊椤甸潰
 - 2.鏁版嵁搴撹〃锛� qm_batch
 ```
--- 鍒涘缓妫�楠屾壒娆¤〃
-CREATE TABLE QM_BATCH (
-    ID VARCHAR(50) NOT NULL,
-    BATCH_CODE VARCHAR(50),
-    BATCH_NAME VARCHAR(50),
-    TYP VARCHAR(20),
-    EQP_CODE VARCHAR(50),
-    MAT_CODE VARCHAR(50),
-    JUDGE_CODE VARCHAR(50),
-    BATCH_DATE DATE,
-    ISFLAG VARCHAR(2),
-    ENABLED VARCHAR(2),
-    TOTAL_NUM NUMERIC(22),
-    RESULTS VARCHAR(50),
-    APPROVER VARCHAR(50),
-    AUDITOR VARCHAR(50),
-    CREATER VARCHAR(50),
-    TAB_DATE DATE,
-    VER_NAME VARCHAR(50),
-    VER_CODE VARCHAR(50),
-    ARCH_DATE VARCHAR(50),
-    FLAG VARCHAR(20),
-    TO_MES_DATE DATE,
-    FROM_MES_DATE DATE,
-    DELETED NUMERIC(22),
-    BATCH_DES VARCHAR(200),
-    CATEGORY VARCHAR(2),
-    MAKENO VARCHAR(60),
-    SHIFTEQPNO VARCHAR(32),
-    BOXNO VARCHAR(60),
-    PID VARCHAR(50),
-    REVIEWER VARCHAR(50),
-    RVCOUNT NUMERIC(22),
-    STATE VARCHAR(2),
-    REVIEW_TIME DATE,
-    AUDIT_TIME DATE,
-    SPEC VARCHAR(50),
-    APPROVE_TIME DATE,
-    UNIT VARCHAR(50),
-    ARRIVAL_TIME DATE,
-    STORAGE_PLACE VARCHAR(50),
-    CHECKER VARCHAR(20),
-    RECEIVE_TIME DATE,
-    INSP_TIME DATE,
-    STORER VARCHAR(50),
-    ISVERIFY VARCHAR(10),
-    ISCHK VARCHAR(10),
-    BAK1 VARCHAR(100),
-    BAK2 VARCHAR(100),
-    CONSTRAINT PK_QM_BATCH PRIMARY KEY (ID)
-);
-
--- 娣诲姞娉ㄩ噴锛堝彲閫夛級
-COMMENT ON TABLE QM_BATCH IS '妫�楠屾壒娆¤〃';
-
-COMMENT ON COLUMN QM_BATCH.ID IS '缂栫爜';
-COMMENT ON COLUMN QM_BATCH.BATCH_CODE IS '鎵规浠g爜';
-COMMENT ON COLUMN QM_BATCH.BATCH_NAME IS '鎵规鍚嶇О';
-COMMENT ON COLUMN QM_BATCH.TYP IS 'A-鍒朵笣 B-鎴愬瀷 C-鍗峰寘 D-灏佺 E-绯栭鏂�';
-COMMENT ON COLUMN QM_BATCH.EQP_CODE IS '鏈哄彴浠g爜';
-COMMENT ON COLUMN QM_BATCH.MAT_CODE IS '鐗屽彿';
-COMMENT ON COLUMN QM_BATCH.JUDGE_CODE IS '鍒ゅ畾渚濇嵁浠g爜';
-COMMENT ON COLUMN QM_BATCH.BATCH_DATE IS '鎵规鐢熸垚鏃ユ湡';
-COMMENT ON COLUMN QM_BATCH.ISFLAG IS '浣跨敤鏍囧織';
-COMMENT ON COLUMN QM_BATCH.ENABLED IS '鍚敤鏍囧織';
-COMMENT ON COLUMN QM_BATCH.TOTAL_NUM IS '鍒拌揣鎬婚噺';
-COMMENT ON COLUMN QM_BATCH.RESULTS IS '缁煎悎鍒ゅ畾';
-COMMENT ON COLUMN QM_BATCH.APPROVER IS '鎵瑰噯浜�';
-COMMENT ON COLUMN QM_BATCH.AUDITOR IS '瀹℃牳浜�';
-COMMENT ON COLUMN QM_BATCH.CREATER IS '鍒涘缓浜�';
-COMMENT ON COLUMN QM_BATCH.TAB_DATE IS '鍒惰〃鏃ユ湡';
-COMMENT ON COLUMN QM_BATCH.VER_NAME IS '鐗堟湰鍚嶇О';
-COMMENT ON COLUMN QM_BATCH.VER_CODE IS '鐗堟湰缂栧彿';
-COMMENT ON COLUMN QM_BATCH.ARCH_DATE IS '淇濆瓨鏈�';
-COMMENT ON COLUMN QM_BATCH.FLAG IS '0-鏈笂浼爉es,1-宸蹭笂浼�, 3-浠嶮ES涓嬭浇';
-COMMENT ON COLUMN QM_BATCH.TO_MES_DATE IS '涓婁紶MES鏃堕棿';
-COMMENT ON COLUMN QM_BATCH.FROM_MES_DATE IS '浠嶮ES鏃堕棿涓嬭浇';
-COMMENT ON COLUMN QM_BATCH.DELETED IS '鍒犻櫎鏍囧織';
-COMMENT ON COLUMN QM_BATCH.BATCH_DES IS '鎵规鎻忚堪';
-COMMENT ON COLUMN QM_BATCH.CATEGORY IS '绫诲埆 0锛氭垚鍝� 1锛氳緟鏉�';
-COMMENT ON COLUMN QM_BATCH.MAKENO IS '鍗峰埗宸ュ彿';
-COMMENT ON COLUMN QM_BATCH.SHIFTEQPNO IS '鐝鏈哄彿';
-COMMENT ON COLUMN QM_BATCH.BOXNO IS '瑁呯鍙�';
-COMMENT ON COLUMN QM_BATCH.PID IS '鐖舵壒娆″彿';
-COMMENT ON COLUMN QM_BATCH.REVIEWER IS '澶嶆牳浜�';
-COMMENT ON COLUMN QM_BATCH.RVCOUNT IS '澶嶆娆℃暟';
-COMMENT ON COLUMN QM_BATCH.STATE IS '鎵规鐘舵��';
-COMMENT ON COLUMN QM_BATCH.REVIEW_TIME IS '澶嶆牳鏃ユ湡';
-COMMENT ON COLUMN QM_BATCH.AUDIT_TIME IS '瀹℃牳鏃ユ湡';
-COMMENT ON COLUMN QM_BATCH.SPEC IS '瑙勬牸';
-COMMENT ON COLUMN QM_BATCH.APPROVE_TIME IS '鎵瑰噯鏃堕棿';
-COMMENT ON COLUMN QM_BATCH.UNIT IS '鍒拌揣鍗曚綅';
-COMMENT ON COLUMN QM_BATCH.ARRIVAL_TIME IS '鍒拌揣鏃ユ湡';
-COMMENT ON COLUMN QM_BATCH.STORAGE_PLACE IS '瀛樻斁鍦扮偣';
-COMMENT ON COLUMN QM_BATCH.CHECKER IS '妫�楠屽憳';
-COMMENT ON COLUMN QM_BATCH.RECEIVE_TIME IS '鎺ュ崟鏃ユ湡';
-COMMENT ON COLUMN QM_BATCH.INSP_TIME IS '鎶ユ鏃ユ湡';
-COMMENT ON COLUMN QM_BATCH.STORER IS '浠撳簱淇濈鍛�';
-COMMENT ON COLUMN QM_BATCH.ISVERIFY IS '鏄惁楠岃瘉';
-COMMENT ON COLUMN QM_BATCH.ISCHK IS '鏄惁妫�楠�';
-COMMENT ON COLUMN QM_BATCH.BAK1 IS '澶囩敤1';
-COMMENT ON COLUMN QM_BATCH.BAK2 IS '澶囩敤2';
-
--- 鍒涘缓绱㈠紩锛堟牴鎹父鐢ㄦ煡璇㈡潯浠跺垱寤猴級
-CREATE INDEX IDX_QM_BATCH_BATCH_CODE ON QM_BATCH(BATCH_CODE);
-CREATE INDEX IDX_QM_BATCH_BATCH_DATE ON QM_BATCH(BATCH_DATE);
-CREATE INDEX IDX_QM_BATCH_TYP ON QM_BATCH(TYP);
-CREATE INDEX IDX_QM_BATCH_MAT_CODE ON QM_BATCH(MAT_CODE);
 ```
+
+#### 20260302
+- 寰呭姙浜嬮」
+- [ ] 鐩墠鍌ㄤ笣鏌滀俊鎭俊鎭病鏈変粠瑙嗗浘涓幏鍙栵紝浣跨敤oracle_store_silk琛ㄦā鎷�
+
diff --git a/ruoyi-plus-soybean/src/router/elegant/imports.ts b/ruoyi-plus-soybean/src/router/elegant/imports.ts
index c533376..4751a41 100755
--- a/ruoyi-plus-soybean/src/router/elegant/imports.ts
+++ b/ruoyi-plus-soybean/src/router/elegant/imports.ts
@@ -23,13 +23,16 @@
   "social-callback": () => import("@/views/_builtin/social-callback/index.vue"),
   "user-center": () => import("@/views/_builtin/user-center/index.vue"),
   about: () => import("@/views/about/index.vue"),
+  "analy_feed-match": () => import("@/views/analy/feed-match/index.vue"),
   analy_hoister: () => import("@/views/analy/hoister/index.vue"),
   "analy_output-analy": () => import("@/views/analy/output-analy/index.vue"),
   analy_packer: () => import("@/views/analy/packer/index.vue"),
   analy_roller: () => import("@/views/analy/roller/index.vue"),
+  "analy_store-silk": () => import("@/views/analy/store-silk/index.vue"),
   demo_demo: () => import("@/views/demo/demo/index.vue"),
   demo_tree: () => import("@/views/demo/tree/index.vue"),
   home: () => import("@/views/home/index.vue"),
+  md_shift: () => import("@/views/md/shift/index.vue"),
   monitor_cache: () => import("@/views/monitor/cache/index.vue"),
   monitor_logininfor: () => import("@/views/monitor/logininfor/index.vue"),
   monitor_online: () => import("@/views/monitor/online/index.vue"),
diff --git a/ruoyi-plus-soybean/src/router/elegant/routes.ts b/ruoyi-plus-soybean/src/router/elegant/routes.ts
index 6a6900a..274f98a 100755
--- a/ruoyi-plus-soybean/src/router/elegant/routes.ts
+++ b/ruoyi-plus-soybean/src/router/elegant/routes.ts
@@ -60,6 +60,15 @@
     },
     children: [
       {
+        name: 'analy_feed-match',
+        path: '/analy/feed-match',
+        component: 'view.analy_feed-match',
+        meta: {
+          title: 'analy_feed-match',
+          i18nKey: 'route.analy_feed-match'
+        }
+      },
+      {
         name: 'analy_hoister',
         path: '/analy/hoister',
         component: 'view.analy_hoister',
@@ -93,6 +102,15 @@
         meta: {
           title: 'analy_roller',
           i18nKey: 'route.analy_roller'
+        }
+      },
+      {
+        name: 'analy_store-silk',
+        path: '/analy/store-silk',
+        component: 'view.analy_store-silk',
+        meta: {
+          title: 'analy_store-silk',
+          i18nKey: 'route.analy_store-silk'
         }
       }
     ]
@@ -163,6 +181,26 @@
     }
   },
   {
+    name: 'md',
+    path: '/md',
+    component: 'layout.base',
+    meta: {
+      title: 'md',
+      i18nKey: 'route.md'
+    },
+    children: [
+      {
+        name: 'md_shift',
+        path: '/md/shift',
+        component: 'view.md_shift',
+        meta: {
+          title: 'md_shift',
+          i18nKey: 'route.md_shift'
+        }
+      }
+    ]
+  },
+  {
     name: 'monitor',
     path: '/monitor',
     component: 'layout.base',
diff --git a/ruoyi-plus-soybean/src/router/elegant/transform.ts b/ruoyi-plus-soybean/src/router/elegant/transform.ts
index 8a15977..7459606 100755
--- a/ruoyi-plus-soybean/src/router/elegant/transform.ts
+++ b/ruoyi-plus-soybean/src/router/elegant/transform.ts
@@ -172,16 +172,20 @@
   "500": "/500",
   "about": "/about",
   "analy": "/analy",
+  "analy_feed-match": "/analy/feed-match",
   "analy_hoister": "/analy/hoister",
   "analy_output-analy": "/analy/output-analy",
   "analy_packer": "/analy/packer",
   "analy_roller": "/analy/roller",
+  "analy_store-silk": "/analy/store-silk",
   "demo": "/demo",
   "demo_demo": "/demo/demo",
   "demo_tree": "/demo/tree",
   "home": "/home",
   "iframe-page": "/iframe-page/:url",
   "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
+  "md": "/md",
+  "md_shift": "/md/shift",
   "monitor": "/monitor",
   "monitor_cache": "/monitor/cache",
   "monitor_logininfor": "/monitor/logininfor",
diff --git a/ruoyi-plus-soybean/src/service/api/analy/feed-match.ts b/ruoyi-plus-soybean/src/service/api/analy/feed-match.ts
new file mode 100644
index 0000000..5fa81eb
--- /dev/null
+++ b/ruoyi-plus-soybean/src/service/api/analy/feed-match.ts
@@ -0,0 +1,35 @@
+import { request } from '@/service/request';
+
+/** 鑾峰彇鍠備笣鏈哄搴斿叧绯诲垪琛� */
+export function fetchGetFeedMatchList (params?: Api.Analy.FeedMatchSearchParams) {
+    return request<Api.Analy.FeedMatchList>({
+        url: '/analy/feedMatch/list',
+        method: 'get',
+        params
+    });
+}
+/** 鏂板鍠備笣鏈哄搴斿叧绯� */
+export function fetchCreateFeedMatch (data: Api.Analy.FeedMatchOperateParams) {
+    return request<boolean>({
+        url: '/analy/feedMatch',
+        method: 'post',
+        data
+    });
+}
+
+/** 淇敼鍠備笣鏈哄搴斿叧绯� */
+export function fetchUpdateFeedMatch (data: Api.Analy.FeedMatchOperateParams) {
+    return request<boolean>({
+        url: '/analy/feedMatch',
+        method: 'put',
+        data
+    });
+}
+
+/** 鎵归噺鍒犻櫎鍠備笣鏈哄搴斿叧绯� */
+export function fetchBatchDeleteFeedMatch (times: CommonType.IdType[]) {
+    return request<boolean>({
+        url: `/analy/feedMatch/${times.join(',')}`,
+        method: 'delete'
+    });
+}
diff --git a/ruoyi-plus-soybean/src/service/api/analy/store-silk.ts b/ruoyi-plus-soybean/src/service/api/analy/store-silk.ts
new file mode 100644
index 0000000..17a3a12
--- /dev/null
+++ b/ruoyi-plus-soybean/src/service/api/analy/store-silk.ts
@@ -0,0 +1,35 @@
+import { request } from '@/service/request';
+
+/** 鑾峰彇鍌ㄤ笣鏌滀骇閲忓垪琛� */
+export function fetchGetStoreSilkList (params?: Api.Analy.StoreSilkSearchParams) {
+    return request<Api.Analy.StoreSilkList>({
+        url: '/analy/storeSilk/list',
+        method: 'get',
+        params
+    });
+}
+/** 鏂板鍌ㄤ笣鏌滀骇閲� */
+export function fetchCreateStoreSilk (data: Api.Analy.StoreSilkOperateParams) {
+    return request<boolean>({
+        url: '/analy/storeSilk',
+        method: 'post',
+        data
+    });
+}
+
+/** 淇敼鍌ㄤ笣鏌滀骇閲� */
+export function fetchUpdateStoreSilk (data: Api.Analy.StoreSilkOperateParams) {
+    return request<boolean>({
+        url: '/analy/storeSilk',
+        method: 'put',
+        data
+    });
+}
+
+/** 鎵归噺鍒犻櫎鍌ㄤ笣鏌滀骇閲� */
+export function fetchBatchDeleteStoreSilk (ids: CommonType.IdType[]) {
+    return request<boolean>({
+        url: `/analy/storeSilk/${ids.join(',')}`,
+        method: 'delete'
+    });
+}
diff --git a/ruoyi-plus-soybean/src/service/api/md/shift.ts b/ruoyi-plus-soybean/src/service/api/md/shift.ts
new file mode 100644
index 0000000..11d2d1e
--- /dev/null
+++ b/ruoyi-plus-soybean/src/service/api/md/shift.ts
@@ -0,0 +1,35 @@
+import { request } from '@/service/request';
+
+/** 鑾峰彇鐝鍒楄〃 */
+export function fetchGetShiftList (params?: Api.Md.ShiftSearchParams) {
+    return request<Api.Md.ShiftList>({
+        url: '/md/shift/list',
+        method: 'get',
+        params
+    });
+}
+/** 鏂板鐝 */
+export function fetchCreateShift (data: Api.Md.ShiftOperateParams) {
+    return request<boolean>({
+        url: '/md/shift',
+        method: 'post',
+        data
+    });
+}
+
+/** 淇敼鐝 */
+export function fetchUpdateShift (data: Api.Md.ShiftOperateParams) {
+    return request<boolean>({
+        url: '/md/shift',
+        method: 'put',
+        data
+    });
+}
+
+/** 鎵归噺鍒犻櫎鐝 */
+export function fetchBatchDeleteShift (ids: CommonType.IdType[]) {
+    return request<boolean>({
+        url: `/md/shift/${ids.join(',')}`,
+        method: 'delete'
+    });
+}
diff --git a/ruoyi-plus-soybean/src/typings/api/analy.feed-match.api.d.ts b/ruoyi-plus-soybean/src/typings/api/analy.feed-match.api.d.ts
new file mode 100644
index 0000000..36a8609
--- /dev/null
+++ b/ruoyi-plus-soybean/src/typings/api/analy.feed-match.api.d.ts
@@ -0,0 +1,118 @@
+/**
+ * Namespace Api
+ *
+ * All backend api type
+ */
+declare namespace Api {
+    /**
+     * namespace Analy
+     *
+     * backend api module: "Analy"
+     */
+    namespace Analy {
+        /** feed match */
+        type FeedMatch = Common.CommonRecord<{
+            /** 鏃堕棿鎴� */
+                time: string; 
+            /** 鐝+鏈哄彴 */
+                key: string; 
+            /** 鏁版嵁鏇存柊鏃堕棿 */
+                dacUpTime: string; 
+            /** 1#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖 */
+                fs11: string; 
+            /** 1#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖 */
+                fs12: string; 
+            /** 2#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖 */
+                fs21: string; 
+            /** 2#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖 */
+                fs22: string; 
+            /** 3#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖 */
+                fs31: string; 
+            /** 3#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖 */
+                fs32: string; 
+            /** 4#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖 */
+                fs41: string; 
+            /** 4#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖 */
+                fs42: string; 
+            /** 1#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe01: number; 
+            /** 2#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe02: number; 
+            /** 3#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe03: number; 
+            /** 4#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe04: number; 
+            /** 5#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe05: number; 
+            /** 6#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe06: number; 
+            /** 7#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe07: number; 
+            /** 8#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe08: number; 
+            /** 9#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe09: number; 
+            /** 10#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe10: number; 
+            /** 11#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe11: number; 
+            /** 12#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬� */
+                pipe12: number; 
+            /** 鍠備笣鏈虹姸鎬� 1-杩炴帴 0-鏂紑 */
+                wsjState: number; 
+            /** 鐝 */
+                shift: number; 
+            /** 鏈哄彴 */
+                equNo: number; 
+            /** 澶囨敞 */
+                remark: string; 
+        }>;
+
+        /** feed match search params */
+        type FeedMatchSearchParams = CommonType.RecordNullable<
+            Pick<
+                Api.Analy.FeedMatch,
+                        | 'time'
+                        | 'key'
+            > &
+            Api.Common.CommonSearchParams
+        >;
+
+        /** feed match operate params */
+        type FeedMatchOperateParams = CommonType.RecordNullable<
+            Pick<
+                Api.Analy.FeedMatch,
+                        | 'time'
+                        | 'key'
+                        | 'dacUpTime'
+                        | 'fs11'
+                        | 'fs12'
+                        | 'fs21'
+                        | 'fs22'
+                        | 'fs31'
+                        | 'fs32'
+                        | 'fs41'
+                        | 'fs42'
+                        | 'pipe01'
+                        | 'pipe02'
+                        | 'pipe03'
+                        | 'pipe04'
+                        | 'pipe05'
+                        | 'pipe06'
+                        | 'pipe07'
+                        | 'pipe08'
+                        | 'pipe09'
+                        | 'pipe10'
+                        | 'pipe11'
+                        | 'pipe12'
+                        | 'wsjState'
+                        | 'shift'
+                        | 'equNo'
+                        | 'remark'
+            >
+        >;
+
+        /** feed match list */
+        type FeedMatchList = Api.Common.PaginatingQueryRecord<FeedMatch>;
+    }
+}
diff --git a/ruoyi-plus-soybean/src/typings/api/analy.store-silk.api.d.ts b/ruoyi-plus-soybean/src/typings/api/analy.store-silk.api.d.ts
new file mode 100644
index 0000000..3b11f30
--- /dev/null
+++ b/ruoyi-plus-soybean/src/typings/api/analy.store-silk.api.d.ts
@@ -0,0 +1,74 @@
+/**
+ * Namespace Api
+ *
+ * All backend api type
+ */
+declare namespace Api {
+    /**
+     * namespace Analy
+     *
+     * backend api module: "Analy"
+     */
+    namespace Analy {
+        /** store silk */
+        type StoreSilk = Common.CommonRecord<{
+            /** id */
+                id: CommonType.IdType; 
+            /** 鐗屽彿 */
+                materialname: string; 
+            /** 鎵规鍙� */
+                batchcode: string; 
+            /** 鎶曟枡鏃ユ湡 */
+                actualstarttime: string; 
+            /** 鎶曟枡閲嶉噺 */
+                jobinput: number; 
+            /** 鎹熻�楅噸閲� */
+                weightloss: number; 
+            /** 鍑哄彾涓濈巼 */
+                slkrate: number; 
+            /** 鍌ㄤ笣鏌滈噸閲� */
+                weight: number; 
+            /** 鍌ㄤ笣鏌滃嚭鏂欏紑濮嬫椂闂� */
+                distimebegin: string; 
+            /** 鍌ㄤ笣鏌滃嚭鏂欑粨鏉熸椂闂� */
+                distimeend: string; 
+            /** 鏌滃瓙鍙�(鏈綅) */
+                siloid: CommonType.IdType; 
+        }>;
+
+        /** store silk search params */
+        type StoreSilkSearchParams = CommonType.RecordNullable<
+            Pick<
+                Api.Analy.StoreSilk,
+                        | 'materialname'
+                        | 'batchcode'
+                        | 'actualstarttime'
+                        | 'distimebegin'
+                        | 'distimeend'
+                        | 'siloid'
+            > &
+            Api.Common.CommonSearchParams
+        >;
+
+        /** store silk operate params */
+        type StoreSilkOperateParams = CommonType.RecordNullable<
+            Pick<
+                Api.Analy.StoreSilk,
+                        | 'id'
+                        | 'materialname'
+                        | 'batchcode'
+                        | 'actualstarttime'
+                        | 'jobinput'
+                        | 'weightloss'
+                        | 'slkrate'
+                        | 'weight'
+                        | 'distimebegin'
+                        | 'distimeend'
+                        | 'siloid'
+            >
+        >;
+
+        /** store silk list */
+        type StoreSilkList = Api.Common.PaginatingQueryRecord<StoreSilk>;
+    }
+}
diff --git a/ruoyi-plus-soybean/src/typings/api/md.shift.api.d.ts b/ruoyi-plus-soybean/src/typings/api/md.shift.api.d.ts
new file mode 100644
index 0000000..9d44c00
--- /dev/null
+++ b/ruoyi-plus-soybean/src/typings/api/md.shift.api.d.ts
@@ -0,0 +1,77 @@
+/**
+ * Namespace Api
+ *
+ * All backend api type
+ */
+declare namespace Api {
+    /**
+     * namespace Md
+     *
+     * backend api module: "Md"
+     */
+    namespace Md {
+        /** shift */
+        type Shift = Common.CommonRecord<{
+            /** ud */
+                id: CommonType.IdType; 
+            /** 杞﹂棿澶栭敭 */
+                wsId: CommonType.IdType; 
+            /** 缂栫爜 */
+                code: string; 
+            /** 鍚嶇О */
+                name: string; 
+            /** 鐝寮�濮嬫椂闂� */
+                stim: string; 
+            /** 鐝缁撴潫鏃堕棿 */
+                etim: string; 
+            /** 鎺掑簭 */
+                seq: number; 
+            /** 鍚敤 */
+                enable: number; 
+            /** 鍒犻櫎 */
+                del: number; 
+            /** 鍒涘缓鐢ㄦ埛 */
+                createUserName: string; 
+            /** 鍒涘缓鏃堕棿 */
+                createUserTime: string; 
+            /** 鏇存柊鐢ㄦ埛 */
+                updateUserName: string; 
+            /** 鏇存柊鏃堕棿 */
+                updateUserTime: string; 
+        }>;
+
+        /** shift search params */
+        type ShiftSearchParams = CommonType.RecordNullable<
+            Pick<
+                Api.Md.Shift,
+                        | 'code'
+                        | 'name'
+                        | 'enable'
+            > &
+            Api.Common.CommonSearchParams
+        >;
+
+        /** shift operate params */
+        type ShiftOperateParams = CommonType.RecordNullable<
+            Pick<
+                Api.Md.Shift,
+                        | 'id'
+                        | 'wsId'
+                        | 'code'
+                        | 'name'
+                        | 'stim'
+                        | 'etim'
+                        | 'seq'
+                        | 'enable'
+                        | 'del'
+                        | 'createUserName'
+                        | 'createUserTime'
+                        | 'updateUserName'
+                        | 'updateUserTime'
+            >
+        >;
+
+        /** shift list */
+        type ShiftList = Api.Common.PaginatingQueryRecord<Shift>;
+    }
+}
diff --git a/ruoyi-plus-soybean/src/typings/components.d.ts b/ruoyi-plus-soybean/src/typings/components.d.ts
index a3a54e0..24c0b46 100755
--- a/ruoyi-plus-soybean/src/typings/components.d.ts
+++ b/ruoyi-plus-soybean/src/typings/components.d.ts
@@ -58,6 +58,7 @@
     IconMdiGithub: typeof import('~icons/mdi/github')['default']
     IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
     IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
+    IconMdiTableLarge: typeof import('~icons/mdi/table-large')['default']
     IconQuillCollapse: typeof import('~icons/quill/collapse')['default']
     IconQuillExpand: typeof import('~icons/quill/expand')['default']
     IconSimpleIconsGitee: typeof import('~icons/simple-icons/gitee')['default']
@@ -76,6 +77,7 @@
     NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
     NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
     NButton: typeof import('naive-ui')['NButton']
+    NButtonGroup: typeof import('naive-ui')['NButtonGroup']
     NCard: typeof import('naive-ui')['NCard']
     NCheckbox: typeof import('naive-ui')['NCheckbox']
     NCode: typeof import('naive-ui')['NCode']
@@ -209,6 +211,7 @@
   const IconMdiGithub: typeof import('~icons/mdi/github')['default']
   const IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
   const IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
+  const IconMdiTableLarge: typeof import('~icons/mdi/table-large')['default']
   const IconQuillCollapse: typeof import('~icons/quill/collapse')['default']
   const IconQuillExpand: typeof import('~icons/quill/expand')['default']
   const IconSimpleIconsGitee: typeof import('~icons/simple-icons/gitee')['default']
@@ -227,6 +230,7 @@
   const NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
   const NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
   const NButton: typeof import('naive-ui')['NButton']
+  const NButtonGroup: typeof import('naive-ui')['NButtonGroup']
   const NCard: typeof import('naive-ui')['NCard']
   const NCheckbox: typeof import('naive-ui')['NCheckbox']
   const NCode: typeof import('naive-ui')['NCode']
diff --git a/ruoyi-plus-soybean/src/typings/elegant-router.d.ts b/ruoyi-plus-soybean/src/typings/elegant-router.d.ts
index 91d02d6..cd2f9bb 100755
--- a/ruoyi-plus-soybean/src/typings/elegant-router.d.ts
+++ b/ruoyi-plus-soybean/src/typings/elegant-router.d.ts
@@ -26,16 +26,20 @@
     "500": "/500";
     "about": "/about";
     "analy": "/analy";
+    "analy_feed-match": "/analy/feed-match";
     "analy_hoister": "/analy/hoister";
     "analy_output-analy": "/analy/output-analy";
     "analy_packer": "/analy/packer";
     "analy_roller": "/analy/roller";
+    "analy_store-silk": "/analy/store-silk";
     "demo": "/demo";
     "demo_demo": "/demo/demo";
     "demo_tree": "/demo/tree";
     "home": "/home";
     "iframe-page": "/iframe-page/:url";
     "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
+    "md": "/md";
+    "md_shift": "/md/shift";
     "monitor": "/monitor";
     "monitor_cache": "/monitor/cache";
     "monitor_logininfor": "/monitor/logininfor";
@@ -105,6 +109,7 @@
     | "home"
     | "iframe-page"
     | "login"
+    | "md"
     | "monitor"
     | "qm"
     | "social-callback"
@@ -136,13 +141,16 @@
     | "social-callback"
     | "user-center"
     | "about"
+    | "analy_feed-match"
     | "analy_hoister"
     | "analy_output-analy"
     | "analy_packer"
     | "analy_roller"
+    | "analy_store-silk"
     | "demo_demo"
     | "demo_tree"
     | "home"
+    | "md_shift"
     | "monitor_cache"
     | "monitor_logininfor"
     | "monitor_online"
diff --git a/ruoyi-plus-soybean/src/views/analy/feed-match/index.vue b/ruoyi-plus-soybean/src/views/analy/feed-match/index.vue
new file mode 100644
index 0000000..f76c41a
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/analy/feed-match/index.vue
@@ -0,0 +1,337 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { NDivider } from 'naive-ui';
+import { fetchBatchDeleteFeedMatch, fetchGetFeedMatchList } from '@/service/api/analy/feed-match';
+import { useAppStore } from '@/store/modules/app';
+import { useAuth } from '@/hooks/business/auth';
+import { useDownload } from '@/hooks/business/download';
+import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
+import { $t } from '@/locales';
+import ButtonIcon from '@/components/custom/button-icon.vue';
+import FeedMatchOperateDrawer from './modules/feed-match-operate-drawer.vue';
+import FeedMatchSearch from './modules/feed-match-search.vue';
+
+defineOptions({
+  name: 'FeedMatchList'
+});
+
+
+const appStore = useAppStore();
+const { download } = useDownload();
+const { hasAuth } = useAuth();
+
+const searchParams = ref<Api.Analy.FeedMatchSearchParams>({
+  pageNum: 1,
+  pageSize: 10,
+  time: null,
+  key: null,
+  params: {}
+});
+
+const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
+  useNaivePaginatedTable({
+  api: () => fetchGetFeedMatchList(searchParams.value),
+  transform: response => defaultTransform(response),
+  onPaginationParamsChange: params => {
+    searchParams.value.pageNum = params.page;
+    searchParams.value.pageSize = params.pageSize;
+  },
+  columns: () => [
+    {
+      type: 'selection',
+      align: 'center',
+      width: 48
+    },
+    {
+      key: 'index',
+      title: $t('common.index'),
+      align: 'center',
+      width: 64,
+      render: (_, index) => index + 1
+    },
+    {
+      key: 'time',
+      title: '鏃堕棿鎴�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'key',
+      title: '鐝+鏈哄彴',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'dacUpTime',
+      title: '鏁版嵁鏇存柊鏃堕棿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'fs11',
+      title: '1#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'fs12',
+      title: '1#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'fs21',
+      title: '2#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'fs22',
+      title: '2#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'fs31',
+      title: '3#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'fs32',
+      title: '3#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'fs41',
+      title: '4#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'fs42',
+      title: '4#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe01',
+      title: '1#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe02',
+      title: '2#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe03',
+      title: '3#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe04',
+      title: '4#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe05',
+      title: '5#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe06',
+      title: '6#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe07',
+      title: '7#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe08',
+      title: '8#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe09',
+      title: '9#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe10',
+      title: '10#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe11',
+      title: '11#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'pipe12',
+      title: '12#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'wsjState',
+      title: '鍠備笣鏈虹姸鎬� 1-杩炴帴 0-鏂紑',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'shift',
+      title: '鐝',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'equNo',
+      title: '鏈哄彴',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'remark',
+      title: '澶囨敞',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'operate',
+      title: $t('common.operate'),
+      align: 'center',
+      width: 130,
+      render: row => {
+        const divider = () => {
+          if (!hasAuth('analy:feedMatch:edit') || !hasAuth('analy:feedMatch:remove')) {
+            return null;
+          }
+          return <NDivider vertical />;
+        };
+
+        const editBtn = () => {
+          if (!hasAuth('analy:feedMatch:edit')) {
+            return null;
+          }
+          return (
+            <ButtonIcon
+              text
+              type="primary"
+              icon="material-symbols:drive-file-rename-outline-outline"
+              tooltipContent={$t('common.edit')}
+              onClick={() => edit(row.time)}
+            />
+          );
+        };
+
+        const deleteBtn = () => {
+          if (!hasAuth('analy:feedMatch:remove')) {
+            return null;
+          }
+          return (
+            <ButtonIcon
+              text
+              type="error"
+              icon="material-symbols:delete-outline"
+              tooltipContent={$t('common.delete')}
+              popconfirmContent={$t('common.confirmDelete')}
+              onPositiveClick={() => handleDelete(row.time)}
+            />
+          );
+        };
+
+        return (
+          <div class="flex-center gap-8px">
+            {editBtn()}
+            {divider()}
+            {deleteBtn()}
+          </div>
+        );
+      }
+    }
+  ]
+});
+
+const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
+  useTableOperate(data, 'time', getData);
+
+async function handleBatchDelete() {
+  // request
+  const { error } = await fetchBatchDeleteFeedMatch(checkedRowKeys.value);
+  if (error) return;
+  onBatchDeleted();
+}
+
+async function handleDelete(time: CommonType.IdType) {
+  // request
+  const { error } = await fetchBatchDeleteFeedMatch([time]);
+  if (error) return;
+  onDeleted();
+}
+
+function edit(time: CommonType.IdType) {
+  handleEdit(time);
+}
+
+function handleExport() {
+  download('/analy/feedMatch/export', searchParams.value, `鍠備笣鏈哄搴斿叧绯籣${new Date().getTime()}.xlsx`);
+}
+</script>
+
+<template>
+  <div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
+    <FeedMatchSearch v-model:model="searchParams" @search="getDataByPage" />
+    <NCard title="鍠備笣鏈哄搴斿叧绯诲垪琛�" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
+      <template #header-extra>
+        <TableHeaderOperation
+          v-model:columns="columnChecks"
+          :disabled-delete="checkedRowKeys.length === 0"
+          :loading="loading"
+          :show-add="hasAuth('analy:feedMatch:add')"
+          :show-delete="hasAuth('analy:feedMatch:remove')"
+          :show-export="hasAuth('analy:feedMatch:export')"
+          @add="handleAdd"
+          @delete="handleBatchDelete"
+          @export="handleExport"
+          @refresh="getData"
+        />
+      </template>
+      <NDataTable
+        v-model:checked-row-keys="checkedRowKeys"
+        :columns="columns"
+        :data="data"
+        size="small"
+        :flex-height="!appStore.isMobile"
+        :scroll-x="scrollX"
+        :loading="loading"
+        remote
+        :row-key="row => row.time"
+        :pagination="mobilePagination"
+        class="sm:h-full"
+      />
+      <FeedMatchOperateDrawer
+        v-model:visible="drawerVisible"
+        :operate-type="operateType"
+        :row-data="editingData"
+        @submitted="getDataByPage"
+      />
+    </NCard>
+  </div>
+</template>
+
+<style scoped></style>
diff --git a/ruoyi-plus-soybean/src/views/analy/feed-match/modules/feed-match-operate-drawer.vue b/ruoyi-plus-soybean/src/views/analy/feed-match/modules/feed-match-operate-drawer.vue
new file mode 100644
index 0000000..77b0221
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/analy/feed-match/modules/feed-match-operate-drawer.vue
@@ -0,0 +1,281 @@
+<script setup lang="ts">
+import { computed, ref, watch } from 'vue';
+import { jsonClone } from '@sa/utils';
+import { fetchCreateFeedMatch, fetchUpdateFeedMatch } from '@/service/api/analy/feed-match';
+import { useFormRules, useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+  name: 'FeedMatchOperateDrawer'
+});
+
+interface Props {
+  /** the type of operation */
+  operateType: NaiveUI.TableOperateType;
+  /** the edit row data */
+  rowData?: Api.Analy.FeedMatch | null;
+}
+
+const props = defineProps<Props>();
+
+interface Emits {
+  (e: 'submitted'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const visible = defineModel<boolean>('visible', {
+  default: false
+});
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+const { createRequiredRule } = useFormRules();
+
+const title = computed(() => {
+  const titles: Record<NaiveUI.TableOperateType, string> = {
+    add: '鏂板鍠備笣鏈哄搴斿叧绯�',
+    edit: '缂栬緫鍠備笣鏈哄搴斿叧绯�'
+  };
+  return titles[props.operateType];
+});
+
+type Model = Api.Analy.FeedMatchOperateParams;
+
+const model = ref<Model>(createDefaultModel());
+
+function createDefaultModel(): Model {
+  return {
+      time: null,
+      key: '',
+      dacUpTime: '',
+      fs11: '',
+      fs12: '',
+      fs21: '',
+      fs22: '',
+      fs31: '',
+      fs32: '',
+      fs41: '',
+      fs42: '',
+      pipe01: null,
+      pipe02: null,
+      pipe03: null,
+      pipe04: null,
+      pipe05: null,
+      pipe06: null,
+      pipe07: null,
+      pipe08: null,
+      pipe09: null,
+      pipe10: null,
+      pipe11: null,
+      pipe12: null,
+      wsjState: null,
+      shift: null,
+      equNo: null,
+      remark: ''
+  };
+}
+
+type RuleKey = Extract<
+  keyof Model,
+  | 'time'
+  | 'key'
+>;
+
+const rules: Record<RuleKey, App.Global.FormRule> = {
+  time: createRequiredRule('鏃堕棿鎴充笉鑳戒负绌�'),
+  key: createRequiredRule('鐝+鏈哄彴涓嶈兘涓虹┖'),
+};
+
+function handleUpdateModelWhenEdit() {
+  model.value = createDefaultModel();
+
+  if (props.operateType === 'edit' && props.rowData) {
+    Object.assign(model.value, jsonClone(props.rowData));
+  }
+}
+
+function closeDrawer() {
+  visible.value = false;
+}
+
+async function handleSubmit() {
+  await validate();
+
+  const { time, key, dacUpTime, fs11, fs12, fs21, fs22, fs31, fs32, fs41, fs42, pipe01, pipe02, pipe03, pipe04, pipe05, pipe06, pipe07, pipe08, pipe09, pipe10, pipe11, pipe12, wsjState, shift, equNo, remark } = model.value;
+
+  // request
+  if (props.operateType === 'add') {
+    const { error } = await fetchCreateFeedMatch({ time, key, dacUpTime, fs11, fs12, fs21, fs22, fs31, fs32, fs41, fs42, pipe01, pipe02, pipe03, pipe04, pipe05, pipe06, pipe07, pipe08, pipe09, pipe10, pipe11, pipe12, wsjState, shift, equNo, remark });
+    if (error) return;
+  }
+
+  if (props.operateType === 'edit') {
+    const { error } = await fetchUpdateFeedMatch({ time, key, dacUpTime, fs11, fs12, fs21, fs22, fs31, fs32, fs41, fs42, pipe01, pipe02, pipe03, pipe04, pipe05, pipe06, pipe07, pipe08, pipe09, pipe10, pipe11, pipe12, wsjState, shift, equNo, remark });
+    if (error) return;
+  }
+
+  window.$message?.success($t('common.updateSuccess'));
+  closeDrawer();
+  emit('submitted');
+}
+
+watch(visible, () => {
+  if (visible.value) {
+    handleUpdateModelWhenEdit();
+    restoreValidation();
+  }
+});
+</script>
+
+<template>
+  <NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
+    <NDrawerContent :title="title" :native-scrollbar="false" closable>
+      <NForm ref="formRef" :model="model" :rules="rules">
+        <NFormItem label="鏃堕棿鎴�" path="time">
+          <NDatePicker
+            v-model:formatted-value="model.time"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="鐝+鏈哄彴" path="key">
+          <NInput
+            v-model:value="model.key"
+            :rows="3"
+            type="textarea"
+            placeholder="璇疯緭鍏ョ彮娆�+鏈哄彴"
+          />
+        </NFormItem>
+        <NFormItem label="鏁版嵁鏇存柊鏃堕棿" path="dacUpTime">
+          <NInput
+            v-model:value="model.dacUpTime"
+            :rows="3"
+            type="textarea"
+            placeholder="璇疯緭鍏ユ暟鎹洿鏂版椂闂�"
+          />
+        </NFormItem>
+        <NFormItem label="1#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖" path="fs11">
+          <NInput
+            v-model:value="model.fs11"
+            :rows="3"
+            type="textarea"
+            placeholder="璇疯緭鍏�1#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖"
+          />
+        </NFormItem>
+        <NFormItem label="1#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖" path="fs12">
+          <NInput
+            v-model:value="model.fs12"
+            :rows="3"
+            type="textarea"
+            placeholder="璇疯緭鍏�1#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖"
+          />
+        </NFormItem>
+        <NFormItem label="2#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖" path="fs21">
+          <NInput
+            v-model:value="model.fs21"
+            :rows="3"
+            type="textarea"
+            placeholder="璇疯緭鍏�2#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖"
+          />
+        </NFormItem>
+        <NFormItem label="2#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖" path="fs22">
+          <NInput
+            v-model:value="model.fs22"
+            :rows="3"
+            type="textarea"
+            placeholder="璇疯緭鍏�2#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖"
+          />
+        </NFormItem>
+        <NFormItem label="3#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖" path="fs31">
+          <NInput
+            v-model:value="model.fs31"
+            :rows="3"
+            type="textarea"
+            placeholder="璇疯緭鍏�3#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖"
+          />
+        </NFormItem>
+        <NFormItem label="3#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖" path="fs32">
+          <NInput
+            v-model:value="model.fs32"
+            :rows="3"
+            type="textarea"
+            placeholder="璇疯緭鍏�3#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖"
+          />
+        </NFormItem>
+        <NFormItem label="4#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖" path="fs41">
+          <NInput
+            v-model:value="model.fs41"
+            :rows="3"
+            type="textarea"
+            placeholder="璇疯緭鍏�4#鍠備笣鏈哄搴旂殑绗竴涓偍涓濇煖"
+          />
+        </NFormItem>
+        <NFormItem label="4#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖" path="fs42">
+          <NInput
+            v-model:value="model.fs42"
+            :rows="3"
+            type="textarea"
+            placeholder="璇疯緭鍏�4#鍠備笣鏈哄搴旂殑绗簩涓偍涓濇煖"
+          />
+        </NFormItem>
+        <NFormItem label="1#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe01">
+          <NInput v-model:value="model.pipe01" placeholder="璇疯緭鍏�1#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="2#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe02">
+          <NInput v-model:value="model.pipe02" placeholder="璇疯緭鍏�2#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="3#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe03">
+          <NInput v-model:value="model.pipe03" placeholder="璇疯緭鍏�3#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="4#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe04">
+          <NInput v-model:value="model.pipe04" placeholder="璇疯緭鍏�4#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="5#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe05">
+          <NInput v-model:value="model.pipe05" placeholder="璇疯緭鍏�5#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="6#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe06">
+          <NInput v-model:value="model.pipe06" placeholder="璇疯緭鍏�6#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="7#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe07">
+          <NInput v-model:value="model.pipe07" placeholder="璇疯緭鍏�7#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="8#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe08">
+          <NInput v-model:value="model.pipe08" placeholder="璇疯緭鍏�8#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="9#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe09">
+          <NInput v-model:value="model.pipe09" placeholder="璇疯緭鍏�9#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="10#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe10">
+          <NInput v-model:value="model.pipe10" placeholder="璇疯緭鍏�10#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="11#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe11">
+          <NInput v-model:value="model.pipe11" placeholder="璇疯緭鍏�11#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="12#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" path="pipe12">
+          <NInput v-model:value="model.pipe12" placeholder="璇疯緭鍏�12#鏈虹粍瀵瑰簲鐨勫杺涓濇満鍜岀閬�" />
+        </NFormItem>
+        <NFormItem label="鍠備笣鏈虹姸鎬� 1-杩炴帴 0-鏂紑" path="wsjState">
+          <NInput v-model:value="model.wsjState" placeholder="璇疯緭鍏ュ杺涓濇満鐘舵�� 1-杩炴帴 0-鏂紑" />
+        </NFormItem>
+        <NFormItem label="鐝" path="shift">
+          <NInput v-model:value="model.shift" placeholder="璇疯緭鍏ョ彮娆�" />
+        </NFormItem>
+        <NFormItem label="鏈哄彴" path="equNo">
+          <NInput v-model:value="model.equNo" placeholder="璇疯緭鍏ユ満鍙�" />
+        </NFormItem>
+        <NFormItem label="澶囨敞" path="remark">
+          <NInput v-model:value="model.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </NFormItem>
+      </NForm>
+      <template #footer>
+        <NSpace :size="16">
+          <NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
+          <NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
+        </NSpace>
+      </template>
+    </NDrawerContent>
+  </NDrawer>
+</template>
+
+<style scoped></style>
diff --git a/ruoyi-plus-soybean/src/views/analy/feed-match/modules/feed-match-search.vue b/ruoyi-plus-soybean/src/views/analy/feed-match/modules/feed-match-search.vue
new file mode 100644
index 0000000..4482f5d
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/analy/feed-match/modules/feed-match-search.vue
@@ -0,0 +1,79 @@
+<script setup lang="ts">
+import { toRaw } from 'vue';
+import { jsonClone } from '@sa/utils';
+import { useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+  name: 'FeedMatchSearch'
+});
+
+interface Emits {
+  (e: 'search'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+
+const model = defineModel<Api.Analy.FeedMatchSearchParams>('model', { required: true });
+
+const defaultModel = jsonClone(toRaw(model.value));
+
+function resetModel() {
+  Object.assign(model.value, defaultModel);
+}
+
+async function reset() {
+  await restoreValidation();
+  resetModel();
+  emit('search');
+}
+
+async function search() {
+  await validate();
+  emit('search');
+}
+</script>
+
+<template>
+  <NCard :bordered="false" size="small" class="card-wrapper">
+    <NCollapse>
+      <NCollapseItem :title="$t('common.search')" name="analy-feed-match-search">
+        <NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
+          <NGrid responsive="screen" item-responsive>
+            <NFormItemGi span="24 s:12 m:6" label="鏃堕棿鎴�" label-width="auto" path="time" class="pr-24px">
+              <NDatePicker
+                v-model:formatted-value="model.time"
+                type="datetime"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                clearable
+              />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:6" label="鐝+鏈哄彴" label-width="auto" path="key" class="pr-24px">
+              <NInput v-model:value="model.key" placeholder="璇疯緭鍏ョ彮娆�+鏈哄彴" />
+            </NFormItemGi>
+            <NFormItemGi :show-feedback="false" span="24" class="pr-24px">
+              <NSpace class="w-full" justify="end">
+                <NButton @click="reset">
+                  <template #icon>
+                    <icon-ic-round-refresh class="text-icon" />
+                  </template>
+                  {{ $t('common.reset') }}
+                </NButton>
+                <NButton type="primary" ghost @click="search">
+                  <template #icon>
+                    <icon-ic-round-search class="text-icon" />
+                  </template>
+                  {{ $t('common.search') }}
+                </NButton>
+              </NSpace>
+            </NFormItemGi>
+          </NGrid>
+        </NForm>
+      </NCollapseItem>
+    </NCollapse>
+  </NCard>
+</template>
+
+<style scoped></style>
diff --git a/ruoyi-plus-soybean/src/views/analy/store-silk/index.vue b/ruoyi-plus-soybean/src/views/analy/store-silk/index.vue
new file mode 100644
index 0000000..addf095
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/analy/store-silk/index.vue
@@ -0,0 +1,273 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { NDivider } from 'naive-ui';
+import { fetchBatchDeleteStoreSilk, fetchGetStoreSilkList } from '@/service/api/analy/store-silk';
+import { useAppStore } from '@/store/modules/app';
+import { useAuth } from '@/hooks/business/auth';
+import { useDownload } from '@/hooks/business/download';
+import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
+import { $t } from '@/locales';
+import ButtonIcon from '@/components/custom/button-icon.vue';
+import StoreSilkOperateDrawer from './modules/store-silk-operate-drawer.vue';
+import StoreSilkSearch from './modules/store-silk-search.vue';
+
+defineOptions({
+  name: 'StoreSilkList'
+});
+
+
+const appStore = useAppStore();
+const { download } = useDownload();
+const { hasAuth } = useAuth();
+
+const searchParams = ref<Api.Analy.StoreSilkSearchParams>({
+  pageNum: 1,
+  pageSize: 10,
+  materialname: null,
+  batchcode: null,
+  actualstarttime: null,
+  distimebegin: null,
+  distimeend: null,
+  siloid: null,
+  params: {}
+});
+
+const tableSize = ref<'tiny' | 'small' | 'medium' | 'large'>('small');
+
+function handleTableSizeChange(size: 'tiny' | 'small' | 'medium' | 'large') {
+  tableSize.value = size;
+}
+
+const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
+  useNaivePaginatedTable({
+  api: () => fetchGetStoreSilkList(searchParams.value),
+  transform: response => defaultTransform(response),
+  onPaginationParamsChange: params => {
+    searchParams.value.pageNum = params.page;
+    searchParams.value.pageSize = params.pageSize;
+  },
+  columns: () => [
+    {
+      type: 'selection',
+      align: 'center',
+      width: 48
+    },
+    {
+      key: 'index',
+      title: $t('common.index'),
+      align: 'center',
+      width: 64,
+      render: (_, index) => index + 1
+    },
+    {
+      key: 'materialname',
+      title: '鐗屽彿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'batchcode',
+      title: '鎵规鍙�',
+      align: 'center',
+      minWidth: 130
+    },
+    {
+      key: 'roller',
+      title: '鍗锋帴浜ч噺',
+      align: 'center',
+      minWidth: 100
+    },
+    {
+      key: 'packer',
+      title: '鍖呰浜ч噺',
+      align: 'center',
+      minWidth: 100
+    },
+    {
+      key: 'actualstarttime',
+      title: '鎶曟枡鏃ユ湡',
+      align: 'center',
+      width: 180
+    },
+    {
+      key: 'jobinput',
+      title: '鎶曟枡閲嶉噺',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'weight',
+      title: '鍌ㄤ笣鏌滈噸閲�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'distimebegin',
+      title: '鍌ㄤ笣鏌滃嚭鏂欏紑濮嬫椂闂�',
+      align: 'center',
+      width: 180
+    },
+    {
+      key: 'distimeend',
+      title: '鍌ㄤ笣鏌滃嚭鏂欑粨鏉熸椂闂�',
+      align: 'center',
+      width: 180
+    },
+    {
+      key: 'siloid',
+      title: '鏌滃瓙鍙�(鏈綅)',
+      align: 'center',
+      width: 160
+    },
+    {
+      key: 'operate',
+      title: $t('common.operate'),
+      align: 'center',
+      fixed: 'right',
+      width: 130,
+      render: row => {
+        const divider = () => {
+          if (!hasAuth('analy:storeSilk:edit') || !hasAuth('analy:storeSilk:remove')) {
+            return null;
+          }
+          return <NDivider vertical />;
+        };
+
+        const editBtn = () => {
+          if (!hasAuth('analy:storeSilk:edit')) {
+            return null;
+          }
+          return (
+            <ButtonIcon
+              text
+              type="primary"
+              icon="material-symbols:drive-file-rename-outline-outline"
+              tooltipContent={$t('common.edit')}
+              onClick={() => edit(row.id)}
+            />
+          );
+        };
+
+        const deleteBtn = () => {
+          if (!hasAuth('analy:storeSilk:remove')) {
+            return null;
+          }
+          return (
+            <ButtonIcon
+              text
+              type="error"
+              icon="material-symbols:delete-outline"
+              tooltipContent={$t('common.delete')}
+              popconfirmContent={$t('common.confirmDelete')}
+              onPositiveClick={() => handleDelete(row.id)}
+            />
+          );
+        };
+
+        return (
+          <div class="flex-center gap-8px">
+            {editBtn()}
+            {divider()}
+            {deleteBtn()}
+          </div>
+        );
+      }
+    }
+  ]
+});
+
+const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
+  useTableOperate(data, 'id', getData);
+
+async function handleBatchDelete() {
+  // request
+  const { error } = await fetchBatchDeleteStoreSilk(checkedRowKeys.value);
+  if (error) return;
+  onBatchDeleted();
+}
+
+async function handleDelete(id: CommonType.IdType) {
+  // request
+  const { error } = await fetchBatchDeleteStoreSilk([id]);
+  if (error) return;
+  onDeleted();
+}
+
+function edit(id: CommonType.IdType) {
+  handleEdit(id);
+}
+
+function handleExport() {
+  download('/analy/storeSilk/export', searchParams.value, `鍌ㄤ笣鏌滀骇閲廮${new Date().getTime()}.xlsx`);
+}
+</script>
+
+<template>
+  <div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
+    <StoreSilkSearch v-model:model="searchParams" @search="getDataByPage" />
+    <NCard title="鍌ㄤ笣鏌滀骇閲忓垪琛�" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
+      <template #header-extra>
+        <TableHeaderOperation
+          v-model:columns="columnChecks"
+          :disabled-delete="checkedRowKeys.length === 0"
+          :loading="loading"
+          :show-add="hasAuth('analy:storeSilk:add')"
+          :show-delete="hasAuth('analy:storeSilk:remove')"
+          :show-export="hasAuth('analy:storeSilk:export')"
+          @add="handleAdd"
+          @delete="handleBatchDelete"
+          @export="handleExport"
+          @refresh="getData"
+        >
+          <template #suffix>
+            <NPopover placement="bottom-end" trigger="click">
+              <template #trigger>
+                <NButton size="small">
+                  <template #icon>
+                    <icon-mdi-table-large class="text-icon" />
+                  </template>
+                  琛ㄦ牸澶у皬
+                </NButton>
+              </template>
+              <NRadioGroup :value="tableSize" @update:value="handleTableSizeChange">
+                <NSpace vertical>
+                  <NRadio value="tiny">mini</NRadio>
+                  <NRadio value="small">灏�</NRadio>
+                  <NRadio value="medium">涓�</NRadio>
+                  <NRadio value="large">澶�</NRadio>
+                </NSpace>
+              </NRadioGroup>
+            </NPopover>
+          </template>
+        </TableHeaderOperation>
+      </template>
+      <NDataTable
+        v-model:checked-row-keys="checkedRowKeys"
+        :columns="columns"
+        :data="data"
+        :size="tableSize"
+        :flex-height="!appStore.isMobile"
+        :scroll-x="scrollX"
+        :loading="loading"
+        remote
+        :row-key="row => row.id"
+        :pagination="mobilePagination"
+        :class="['sm:h-full', tableSize === 'tiny' ? 'table-size-mini' : '']"
+      />
+      <StoreSilkOperateDrawer
+        v-model:visible="drawerVisible"
+        :operate-type="operateType"
+        :row-data="editingData"
+        @submitted="getDataByPage"
+      />
+    </NCard>
+  </div>
+</template>
+
+<style scoped>
+:deep(.table-size-mini .n-data-table-th),
+:deep(.table-size-mini .n-data-table-td) {
+  padding-top: 4px;
+  padding-bottom: 4px;
+}
+</style>
diff --git a/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-operate-drawer.vue b/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-operate-drawer.vue
new file mode 100644
index 0000000..523341c
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-operate-drawer.vue
@@ -0,0 +1,176 @@
+<script setup lang="ts">
+import { computed, ref, watch } from 'vue';
+import { jsonClone } from '@sa/utils';
+import { fetchCreateStoreSilk, fetchUpdateStoreSilk } from '@/service/api/analy/store-silk';
+import { useFormRules, useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+  name: 'StoreSilkOperateDrawer'
+});
+
+interface Props {
+  /** the type of operation */
+  operateType: NaiveUI.TableOperateType;
+  /** the edit row data */
+  rowData?: Api.Analy.StoreSilk | null;
+}
+
+const props = defineProps<Props>();
+
+interface Emits {
+  (e: 'submitted'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const visible = defineModel<boolean>('visible', {
+  default: false
+});
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+const { createRequiredRule } = useFormRules();
+
+const title = computed(() => {
+  const titles: Record<NaiveUI.TableOperateType, string> = {
+    add: '鏂板鍌ㄤ笣鏌滀骇閲�',
+    edit: '缂栬緫鍌ㄤ笣鏌滀骇閲�'
+  };
+  return titles[props.operateType];
+});
+
+type Model = Api.Analy.StoreSilkOperateParams;
+
+const model = ref<Model>(createDefaultModel());
+
+function createDefaultModel(): Model {
+  return {
+      id: null,
+      materialname: '',
+      batchcode: '',
+      actualstarttime: null,
+      jobinput: null,
+      weightloss: null,
+      slkrate: null,
+      weight: null,
+      distimebegin: null,
+      distimeend: null,
+      siloid: ''
+  };
+}
+
+type RuleKey = Extract<
+  keyof Model,
+  | 'id'
+  | 'materialname'
+  | 'batchcode'
+>;
+
+const rules: Record<RuleKey, App.Global.FormRule> = {
+  id: createRequiredRule('id涓嶈兘涓虹┖'),
+  materialname: createRequiredRule('鐗屽彿涓嶈兘涓虹┖'),
+  batchcode: createRequiredRule('鎵规鍙蜂笉鑳戒负绌�'),
+};
+
+function handleUpdateModelWhenEdit() {
+  model.value = createDefaultModel();
+
+  if (props.operateType === 'edit' && props.rowData) {
+    Object.assign(model.value, jsonClone(props.rowData));
+  }
+}
+
+function closeDrawer() {
+  visible.value = false;
+}
+
+async function handleSubmit() {
+  await validate();
+
+  const { id, materialname, batchcode, actualstarttime, jobinput, weightloss, slkrate, weight, distimebegin, distimeend, siloid } = model.value;
+
+  // request
+  if (props.operateType === 'add') {
+    const { error } = await fetchCreateStoreSilk({ materialname, batchcode, actualstarttime, jobinput, weightloss, slkrate, weight, distimebegin, distimeend, siloid });
+    if (error) return;
+  }
+
+  if (props.operateType === 'edit') {
+    const { error } = await fetchUpdateStoreSilk({ id, materialname, batchcode, actualstarttime, jobinput, weightloss, slkrate, weight, distimebegin, distimeend, siloid });
+    if (error) return;
+  }
+
+  window.$message?.success($t('common.updateSuccess'));
+  closeDrawer();
+  emit('submitted');
+}
+
+watch(visible, () => {
+  if (visible.value) {
+    handleUpdateModelWhenEdit();
+    restoreValidation();
+  }
+});
+</script>
+
+<template>
+  <NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
+    <NDrawerContent :title="title" :native-scrollbar="false" closable>
+      <NForm ref="formRef" :model="model" :rules="rules">
+        <NFormItem label="鐗屽彿" path="materialname">
+          <NInput v-model:value="model.materialname" placeholder="璇疯緭鍏ョ墝鍙�" />
+        </NFormItem>
+        <NFormItem label="鎵规鍙�" path="batchcode">
+          <NInput v-model:value="model.batchcode" placeholder="璇疯緭鍏ユ壒娆″彿" />
+        </NFormItem>
+        <NFormItem label="鎶曟枡鏃ユ湡" path="actualstarttime">
+          <NDatePicker
+            v-model:formatted-value="model.actualstarttime"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="鎶曟枡閲嶉噺" path="jobinput">
+          <NInput v-model:value="model.jobinput" placeholder="璇疯緭鍏ユ姇鏂欓噸閲�" />
+        </NFormItem>
+        <NFormItem label="鎹熻�楅噸閲�" path="weightloss">
+          <NInput v-model:value="model.weightloss" placeholder="璇疯緭鍏ユ崯鑰楅噸閲�" />
+        </NFormItem>
+        <NFormItem label="鍑哄彾涓濈巼" path="slkrate">
+          <NInput v-model:value="model.slkrate" placeholder="璇疯緭鍏ュ嚭鍙朵笣鐜�" />
+        </NFormItem>
+        <NFormItem label="鍌ㄤ笣鏌滈噸閲�" path="weight">
+          <NInput v-model:value="model.weight" placeholder="璇疯緭鍏ュ偍涓濇煖閲嶉噺" />
+        </NFormItem>
+        <NFormItem label="鍌ㄤ笣鏌滃嚭鏂欏紑濮嬫椂闂�" path="distimebegin">
+          <NDatePicker
+            v-model:formatted-value="model.distimebegin"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="鍌ㄤ笣鏌滃嚭鏂欑粨鏉熸椂闂�" path="distimeend">
+          <NDatePicker
+            v-model:formatted-value="model.distimeend"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="鏌滃瓙鍙�(鏈綅)" path="siloid">
+          <NInput v-model:value="model.siloid" placeholder="璇疯緭鍏ユ煖瀛愬彿(鏈綅)" />
+        </NFormItem>
+      </NForm>
+      <template #footer>
+        <NSpace :size="16">
+          <NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
+          <NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
+        </NSpace>
+      </template>
+    </NDrawerContent>
+  </NDrawer>
+</template>
+
+<style scoped></style>
diff --git a/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-search.vue b/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-search.vue
new file mode 100644
index 0000000..2c64d93
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-search.vue
@@ -0,0 +1,101 @@
+<script setup lang="ts">
+import { toRaw } from 'vue';
+import { jsonClone } from '@sa/utils';
+import { useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+  name: 'StoreSilkSearch'
+});
+
+interface Emits {
+  (e: 'search'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+
+const model = defineModel<Api.Analy.StoreSilkSearchParams>('model', { required: true });
+
+const defaultModel = jsonClone(toRaw(model.value));
+
+function resetModel() {
+  Object.assign(model.value, defaultModel);
+}
+
+async function reset() {
+  await restoreValidation();
+  resetModel();
+  emit('search');
+}
+
+async function search() {
+  await validate();
+  emit('search');
+}
+</script>
+
+<template>
+  <NCard :bordered="false" size="small" class="card-wrapper">
+    <NCollapse>
+      <NCollapseItem :title="$t('common.search')" name="analy-store-silk-search">
+        <NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
+          <NGrid responsive="screen" item-responsive>
+            <NFormItemGi span="24 s:12 m:6" label="鐗屽彿" label-width="auto" path="materialname" class="pr-24px">
+              <NInput v-model:value="model.materialname" placeholder="璇疯緭鍏ョ墝鍙�" />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:6" label="鎵规鍙�" label-width="auto" path="batchcode" class="pr-24px">
+              <NInput v-model:value="model.batchcode" placeholder="璇疯緭鍏ユ壒娆″彿" />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:6" label="鎶曟枡鏃ユ湡" label-width="auto" path="actualstarttime" class="pr-24px">
+              <NDatePicker
+                v-model:formatted-value="model.actualstarttime"
+                type="datetime"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                clearable
+              />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:6" label="鍌ㄤ笣鏌滃嚭鏂欏紑濮嬫椂闂�" label-width="auto" path="distimebegin" class="pr-24px">
+              <NDatePicker
+                v-model:formatted-value="model.distimebegin"
+                type="datetime"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                clearable
+              />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:6" label="鍌ㄤ笣鏌滃嚭鏂欑粨鏉熸椂闂�" label-width="auto" path="distimeend" class="pr-24px">
+              <NDatePicker
+                v-model:formatted-value="model.distimeend"
+                type="datetime"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                clearable
+              />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:6" label="鏌滃瓙鍙�(鏈綅)" label-width="auto" path="siloid" class="pr-24px">
+              <NInput v-model:value="model.siloid" placeholder="璇疯緭鍏ユ煖瀛愬彿(鏈綅)" />
+            </NFormItemGi>
+            <NFormItemGi :show-feedback="false" span="24" class="pr-24px">
+              <NSpace class="w-full" justify="end">
+                <NButton @click="reset">
+                  <template #icon>
+                    <icon-ic-round-refresh class="text-icon" />
+                  </template>
+                  {{ $t('common.reset') }}
+                </NButton>
+                <NButton type="primary" ghost @click="search">
+                  <template #icon>
+                    <icon-ic-round-search class="text-icon" />
+                  </template>
+                  {{ $t('common.search') }}
+                </NButton>
+              </NSpace>
+            </NFormItemGi>
+          </NGrid>
+        </NForm>
+      </NCollapseItem>
+    </NCollapse>
+  </NCard>
+</template>
+
+<style scoped></style>
diff --git a/ruoyi-plus-soybean/src/views/md/shift/index.vue b/ruoyi-plus-soybean/src/views/md/shift/index.vue
new file mode 100644
index 0000000..d28d6b0
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/md/shift/index.vue
@@ -0,0 +1,254 @@
+<script setup lang="tsx">
+import { ref } from 'vue';
+import { NDivider } from 'naive-ui';
+import { fetchBatchDeleteShift, fetchGetShiftList } from '@/service/api/md/shift';
+import { useAppStore } from '@/store/modules/app';
+import { useAuth } from '@/hooks/business/auth';
+import { useDownload } from '@/hooks/business/download';
+import { defaultTransform, useNaivePaginatedTable, useTableOperate } from '@/hooks/common/table';
+import { $t } from '@/locales';
+import ButtonIcon from '@/components/custom/button-icon.vue';
+import ShiftOperateDrawer from './modules/shift-operate-drawer.vue';
+import ShiftSearch from './modules/shift-search.vue';
+
+defineOptions({
+  name: 'ShiftList'
+});
+
+
+const appStore = useAppStore();
+const { download } = useDownload();
+const { hasAuth } = useAuth();
+
+const searchParams = ref<Api.Md.ShiftSearchParams>({
+  pageNum: 1,
+  pageSize: 10,
+  code: null,
+  name: null,
+  enable: null,
+  params: {}
+});
+
+const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
+  useNaivePaginatedTable({
+  api: () => fetchGetShiftList(searchParams.value),
+  transform: response => defaultTransform(response),
+  onPaginationParamsChange: params => {
+    searchParams.value.pageNum = params.page;
+    searchParams.value.pageSize = params.pageSize;
+  },
+  columns: () => [
+    {
+      type: 'selection',
+      align: 'center',
+      width: 48
+    },
+    {
+      key: 'index',
+      title: $t('common.index'),
+      align: 'center',
+      width: 64,
+      render: (_, index) => index + 1
+    },
+    {
+      key: 'id',
+      title: 'ud',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'wsId',
+      title: '杞﹂棿澶栭敭',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'code',
+      title: '缂栫爜',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'name',
+      title: '鍚嶇О',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'stim',
+      title: '鐝寮�濮嬫椂闂�',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'etim',
+      title: '鐝缁撴潫鏃堕棿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'seq',
+      title: '鎺掑簭',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'enable',
+      title: '鍚敤',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'del',
+      title: '鍒犻櫎',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'createUserName',
+      title: '鍒涘缓鐢ㄦ埛',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'createUserTime',
+      title: '鍒涘缓鏃堕棿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'updateUserName',
+      title: '鏇存柊鐢ㄦ埛',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'updateUserTime',
+      title: '鏇存柊鏃堕棿',
+      align: 'center',
+      minWidth: 120
+    },
+    {
+      key: 'operate',
+      title: $t('common.operate'),
+      align: 'center',
+      width: 130,
+      render: row => {
+        const divider = () => {
+          if (!hasAuth('md:shift:edit') || !hasAuth('md:shift:remove')) {
+            return null;
+          }
+          return <NDivider vertical />;
+        };
+
+        const editBtn = () => {
+          if (!hasAuth('md:shift:edit')) {
+            return null;
+          }
+          return (
+            <ButtonIcon
+              text
+              type="primary"
+              icon="material-symbols:drive-file-rename-outline-outline"
+              tooltipContent={$t('common.edit')}
+              onClick={() => edit(row.id)}
+            />
+          );
+        };
+
+        const deleteBtn = () => {
+          if (!hasAuth('md:shift:remove')) {
+            return null;
+          }
+          return (
+            <ButtonIcon
+              text
+              type="error"
+              icon="material-symbols:delete-outline"
+              tooltipContent={$t('common.delete')}
+              popconfirmContent={$t('common.confirmDelete')}
+              onPositiveClick={() => handleDelete(row.id)}
+            />
+          );
+        };
+
+        return (
+          <div class="flex-center gap-8px">
+            {editBtn()}
+            {divider()}
+            {deleteBtn()}
+          </div>
+        );
+      }
+    }
+  ]
+});
+
+const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
+  useTableOperate(data, 'id', getData);
+
+async function handleBatchDelete() {
+  // request
+  const { error } = await fetchBatchDeleteShift(checkedRowKeys.value);
+  if (error) return;
+  onBatchDeleted();
+}
+
+async function handleDelete(id: CommonType.IdType) {
+  // request
+  const { error } = await fetchBatchDeleteShift([id]);
+  if (error) return;
+  onDeleted();
+}
+
+function edit(id: CommonType.IdType) {
+  handleEdit(id);
+}
+
+function handleExport() {
+  download('/md/shift/export', searchParams.value, `鐝_${new Date().getTime()}.xlsx`);
+}
+</script>
+
+<template>
+  <div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
+    <ShiftSearch v-model:model="searchParams" @search="getDataByPage" />
+    <NCard title="鐝鍒楄〃" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
+      <template #header-extra>
+        <TableHeaderOperation
+          v-model:columns="columnChecks"
+          :disabled-delete="checkedRowKeys.length === 0"
+          :loading="loading"
+          :show-add="hasAuth('md:shift:add')"
+          :show-delete="hasAuth('md:shift:remove')"
+          :show-export="hasAuth('md:shift:export')"
+          @add="handleAdd"
+          @delete="handleBatchDelete"
+          @export="handleExport"
+          @refresh="getData"
+        />
+      </template>
+      <NDataTable
+        v-model:checked-row-keys="checkedRowKeys"
+        :columns="columns"
+        :data="data"
+        size="small"
+        :flex-height="!appStore.isMobile"
+        :scroll-x="scrollX"
+        :loading="loading"
+        remote
+        :row-key="row => row.id"
+        :pagination="mobilePagination"
+        class="sm:h-full"
+      />
+      <ShiftOperateDrawer
+        v-model:visible="drawerVisible"
+        :operate-type="operateType"
+        :row-data="editingData"
+        @submitted="getDataByPage"
+      />
+    </NCard>
+  </div>
+</template>
+
+<style scoped></style>
diff --git a/ruoyi-plus-soybean/src/views/md/shift/modules/shift-operate-drawer.vue b/ruoyi-plus-soybean/src/views/md/shift/modules/shift-operate-drawer.vue
new file mode 100644
index 0000000..e98ba6e
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/md/shift/modules/shift-operate-drawer.vue
@@ -0,0 +1,175 @@
+<script setup lang="ts">
+import { computed, ref, watch } from 'vue';
+import { jsonClone } from '@sa/utils';
+import { fetchCreateShift, fetchUpdateShift } from '@/service/api/md/shift';
+import { useFormRules, useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+  name: 'ShiftOperateDrawer'
+});
+
+interface Props {
+  /** the type of operation */
+  operateType: NaiveUI.TableOperateType;
+  /** the edit row data */
+  rowData?: Api.Md.Shift | null;
+}
+
+const props = defineProps<Props>();
+
+interface Emits {
+  (e: 'submitted'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const visible = defineModel<boolean>('visible', {
+  default: false
+});
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+const { createRequiredRule } = useFormRules();
+
+const title = computed(() => {
+  const titles: Record<NaiveUI.TableOperateType, string> = {
+    add: '鏂板鐝',
+    edit: '缂栬緫鐝'
+  };
+  return titles[props.operateType];
+});
+
+type Model = Api.Md.ShiftOperateParams;
+
+const model = ref<Model>(createDefaultModel());
+
+function createDefaultModel(): Model {
+  return {
+      id: '',
+      wsId: '',
+      code: '',
+      name: '',
+      stim: '',
+      etim: '',
+      seq: null,
+      enable: null,
+      del: null,
+      createUserName: '',
+      createUserTime: null,
+      updateUserName: '',
+      updateUserTime: null
+  };
+}
+
+type RuleKey = Extract<
+  keyof Model,
+  | 'id'
+>;
+
+const rules: Record<RuleKey, App.Global.FormRule> = {
+  id: createRequiredRule('ud涓嶈兘涓虹┖'),
+};
+
+function handleUpdateModelWhenEdit() {
+  model.value = createDefaultModel();
+
+  if (props.operateType === 'edit' && props.rowData) {
+    Object.assign(model.value, jsonClone(props.rowData));
+  }
+}
+
+function closeDrawer() {
+  visible.value = false;
+}
+
+async function handleSubmit() {
+  await validate();
+
+  const { id, wsId, code, name, stim, etim, seq, enable, del, createUserName, createUserTime, updateUserName, updateUserTime } = model.value;
+
+  // request
+  if (props.operateType === 'add') {
+    const { error } = await fetchCreateShift({ wsId, code, name, stim, etim, seq, enable, del, createUserName, createUserTime, updateUserName, updateUserTime });
+    if (error) return;
+  }
+
+  if (props.operateType === 'edit') {
+    const { error } = await fetchUpdateShift({ id, wsId, code, name, stim, etim, seq, enable, del, createUserName, createUserTime, updateUserName, updateUserTime });
+    if (error) return;
+  }
+
+  window.$message?.success($t('common.updateSuccess'));
+  closeDrawer();
+  emit('submitted');
+}
+
+watch(visible, () => {
+  if (visible.value) {
+    handleUpdateModelWhenEdit();
+    restoreValidation();
+  }
+});
+</script>
+
+<template>
+  <NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
+    <NDrawerContent :title="title" :native-scrollbar="false" closable>
+      <NForm ref="formRef" :model="model" :rules="rules">
+        <NFormItem label="杞﹂棿澶栭敭" path="wsId">
+          <NInput v-model:value="model.wsId" placeholder="璇疯緭鍏ヨ溅闂村閿�" />
+        </NFormItem>
+        <NFormItem label="缂栫爜" path="code">
+          <NInput v-model:value="model.code" placeholder="璇疯緭鍏ョ紪鐮�" />
+        </NFormItem>
+        <NFormItem label="鍚嶇О" path="name">
+          <NInput v-model:value="model.name" placeholder="璇疯緭鍏ュ悕绉�" />
+        </NFormItem>
+        <NFormItem label="鐝寮�濮嬫椂闂�" path="stim">
+          <NInput v-model:value="model.stim" placeholder="璇疯緭鍏ョ彮娆″紑濮嬫椂闂�" />
+        </NFormItem>
+        <NFormItem label="鐝缁撴潫鏃堕棿" path="etim">
+          <NInput v-model:value="model.etim" placeholder="璇疯緭鍏ョ彮娆$粨鏉熸椂闂�" />
+        </NFormItem>
+        <NFormItem label="鎺掑簭" path="seq">
+          <NInput v-model:value="model.seq" placeholder="璇疯緭鍏ユ帓搴�" />
+        </NFormItem>
+        <NFormItem label="鍚敤" path="enable">
+          <NInput v-model:value="model.enable" placeholder="璇疯緭鍏ュ惎鐢�" />
+        </NFormItem>
+        <NFormItem label="鍒犻櫎" path="del">
+          <NInput v-model:value="model.del" placeholder="璇疯緭鍏ュ垹闄�" />
+        </NFormItem>
+        <NFormItem label="鍒涘缓鐢ㄦ埛" path="createUserName">
+          <NInput v-model:value="model.createUserName" placeholder="璇疯緭鍏ュ垱寤虹敤鎴�" />
+        </NFormItem>
+        <NFormItem label="鍒涘缓鏃堕棿" path="createUserTime">
+          <NDatePicker
+            v-model:formatted-value="model.createUserTime"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+        <NFormItem label="鏇存柊鐢ㄦ埛" path="updateUserName">
+          <NInput v-model:value="model.updateUserName" placeholder="璇疯緭鍏ユ洿鏂扮敤鎴�" />
+        </NFormItem>
+        <NFormItem label="鏇存柊鏃堕棿" path="updateUserTime">
+          <NDatePicker
+            v-model:formatted-value="model.updateUserTime"
+            type="datetime"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            clearable
+          />
+        </NFormItem>
+      </NForm>
+      <template #footer>
+        <NSpace :size="16">
+          <NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
+          <NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
+        </NSpace>
+      </template>
+    </NDrawerContent>
+  </NDrawer>
+</template>
+
+<style scoped></style>
diff --git a/ruoyi-plus-soybean/src/views/md/shift/modules/shift-search.vue b/ruoyi-plus-soybean/src/views/md/shift/modules/shift-search.vue
new file mode 100644
index 0000000..d544a66
--- /dev/null
+++ b/ruoyi-plus-soybean/src/views/md/shift/modules/shift-search.vue
@@ -0,0 +1,77 @@
+<script setup lang="ts">
+import { toRaw } from 'vue';
+import { jsonClone } from '@sa/utils';
+import { useNaiveForm } from '@/hooks/common/form';
+import { $t } from '@/locales';
+
+defineOptions({
+  name: 'ShiftSearch'
+});
+
+interface Emits {
+  (e: 'search'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const { formRef, validate, restoreValidation } = useNaiveForm();
+
+const model = defineModel<Api.Md.ShiftSearchParams>('model', { required: true });
+
+const defaultModel = jsonClone(toRaw(model.value));
+
+function resetModel() {
+  Object.assign(model.value, defaultModel);
+}
+
+async function reset() {
+  await restoreValidation();
+  resetModel();
+  emit('search');
+}
+
+async function search() {
+  await validate();
+  emit('search');
+}
+</script>
+
+<template>
+  <NCard :bordered="false" size="small" class="card-wrapper">
+    <NCollapse>
+      <NCollapseItem :title="$t('common.search')" name="md-shift-search">
+        <NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
+          <NGrid responsive="screen" item-responsive>
+            <NFormItemGi span="24 s:12 m:6" label="缂栫爜" label-width="auto" path="code" class="pr-24px">
+              <NInput v-model:value="model.code" placeholder="璇疯緭鍏ョ紪鐮�" />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:6" label="鍚嶇О" label-width="auto" path="name" class="pr-24px">
+              <NInput v-model:value="model.name" placeholder="璇疯緭鍏ュ悕绉�" />
+            </NFormItemGi>
+            <NFormItemGi span="24 s:12 m:6" label="鍚敤" label-width="auto" path="enable" class="pr-24px">
+              <NInput v-model:value="model.enable" placeholder="璇疯緭鍏ュ惎鐢�" />
+            </NFormItemGi>
+            <NFormItemGi :show-feedback="false" span="24" class="pr-24px">
+              <NSpace class="w-full" justify="end">
+                <NButton @click="reset">
+                  <template #icon>
+                    <icon-ic-round-refresh class="text-icon" />
+                  </template>
+                  {{ $t('common.reset') }}
+                </NButton>
+                <NButton type="primary" ghost @click="search">
+                  <template #icon>
+                    <icon-ic-round-search class="text-icon" />
+                  </template>
+                  {{ $t('common.search') }}
+                </NButton>
+              </NSpace>
+            </NFormItemGi>
+          </NGrid>
+        </NForm>
+      </NCollapseItem>
+    </NCollapse>
+  </NCard>
+</template>
+
+<style scoped></style>

--
Gitblit v1.9.3