广丰卷烟厂数采质量分析系统
zhuguifei
2026-03-04 64c2870483568cdeb0206661249a19c5b06de8fe
feat(moudule(qa)): 新增喂丝机对应关系、第三方储丝柜信息视图等管理界面
已修改6个文件
已添加15个文件
2297 ■■■■■ 文件已修改
RuoYi-Vue-Plus/开发日志.md 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/elegant/imports.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/elegant/routes.ts 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/elegant/transform.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/analy/feed-match.ts 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/analy/store-silk.ts 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/md/shift.ts 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/analy.feed-match.api.d.ts 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/analy.store-silk.api.d.ts 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/md.shift.api.d.ts 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/components.d.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/elegant-router.d.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/feed-match/index.vue 337 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/feed-match/modules/feed-match-operate-drawer.vue 281 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/feed-match/modules/feed-match-search.vue 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/store-silk/index.vue 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-operate-drawer.vue 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/store-silk/modules/store-silk-search.vue 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/md/shift/index.vue 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/md/shift/modules/shift-operate-drawer.vue 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/md/shift/modules/shift-search.vue 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/¿ª·¢ÈÕÖ¾.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 '批次代码';
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 '机台代码';
COMMENT ON COLUMN QM_BATCH.MAT_CODE IS '牌号';
COMMENT ON COLUMN QM_BATCH.JUDGE_CODE IS '判定依据代码';
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-未上传mes,1-已上传, 3-从MES下载';
COMMENT ON COLUMN QM_BATCH.TO_MES_DATE IS '上传MES时间';
COMMENT ON COLUMN QM_BATCH.FROM_MES_DATE IS '从MES时间下载';
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表模拟
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"),
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',
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",
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'
    });
}
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'
    });
}
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'
    });
}
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>;
    }
}
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>;
    }
}
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>;
    }
}
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']
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"
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>
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>
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>
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>
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>
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>
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>
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>
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>