wangzhibo
6 天以前 7bd866831780abcf5a59c4cbb7d1be456eea7c99
add shop module
147个文件已添加
20个文件已修改
24880 ■■■■■ 已修改文件
build/cool/eps.d.ts 3347 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
build/cool/eps.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json 9594 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/crud/package.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/vite-plugin/dist/eps/index.d.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/vite-plugin/dist/utils/index.d.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/vite-plugin/package.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/vite-plugin/pnpm-lock.yaml 2269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/vite-plugin/src/config.ts 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/vite-plugin/src/ctx/index.ts 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/vite-plugin/src/eps/index.ts 649 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/vite-plugin/src/index.ts 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/vite-plugin/src/svg/index.ts 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/vite-plugin/src/utils/index.ts 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packages/vite-plugin/types/index.d.ts 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pnpm-lock.yaml 3014 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/index.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/views/complain.vue 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/views/feedback.vue 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/views/goods.vue 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/app/views/version.vue 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/css/animation.scss 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/svg/icon-command.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/svg/icon-fx.svg 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/svg/icon-living.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/svg/icon-message.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/svg/icon-new.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/svg/icon-pending.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/svg/icon-scan.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/svg/icon-system.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/svg/icon-theme.svg 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/svg/icon-track.svg 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/static/svg/icon-upload.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/components/index.vue 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/components/message.vue 526 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/components/session.vue 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/components/tools/emoji.vue 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/config.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/hooks/index.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/hooks/message.ts 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/hooks/notice.tsx 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/hooks/session.ts 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/hooks/socket.ts 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/hooks/tools.ts 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/index.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/audio/ding.wav 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/angry-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/anguished-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/astonished-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/confounded-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/confused-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/crying-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/disappointed-but-relieved-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/disappointed-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/dizzy-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/drooling-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/expressionless-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-savouring-delicious-food.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-screaming-in-fear.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-throwing-a-kiss.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-cold-sweat.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-cowboy-hat.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-finger-covering-closed-lips.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-head-bandage.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-look-of-triumph.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-medical-mask.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-monocle.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-one-eyebrow-raised.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-open-mouth-and-cold-sweat.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-open-mouth-vomiting.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-open-mouth.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-party-horn-and-party-hat.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-pleading-eyes.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-rolling-eyes.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-stuck-out-tongue-and-tightly-closed-eyes.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-stuck-out-tongue-and-winking-eye.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-stuck-out-tongue.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-thermometer.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-with-uneven-eyes-and-wavy-mouth.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/face-without-mouth.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/fearful-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/flushed-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/freezing-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/frowning-face-with-open-mouth.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/grimacing-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/grinning-face-with-one-large-and-one-small-eye.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/grinning-face-with-smiling-eyes.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/grinning-face-with-star-eyes.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/grinning-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/hugging-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/hushed-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/imp.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/kissing-face-with-closed-eyes.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/kissing-face-with-smiling-eyes.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/kissing-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/loudly-crying-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/lying-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/money-mouth-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/nauseated-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/nerd-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/neutral-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/overheated-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/pensive-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/persevering-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/pouting-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/relieved-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/rolling-on-the-floor-laughing.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/serious-face-with-symbols-covering-mouth.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/shocked-face-with-exploding-head.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/sleeping-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/sleepy-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/slightly-frowning-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/slightly-smiling-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/smiling-face-with-halo.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/smiling-face-with-heart-shaped-eyes.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/smiling-face-with-horns.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/smiling-face-with-open-mouth-and-smiling-eyes.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/smiling-face-with-open-mouth-and-tightly-closed-eyes.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/smiling-face-with-open-mouth.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/smiling-face-with-smiling-eyes-and-hand-covering-mouth.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/smiling-face-with-smiling-eyes-and-three-hearts.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/smiling-face-with-smiling-eyes.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/smiling-face-with-sunglasses.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/smirking-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/sneezing-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/thinking-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/tired-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/upside-down-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/weary-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/white-frowning-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/white-smiling-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/winking-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/worried-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/static/emoji/zipper-mouth-face.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/types/index.d.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/cs/utils/index.ts 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/demo/views/home/components/count-category.vue 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/demo/views/home/components/goods-rank.vue 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/demo/views/test/crud.vue 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/components/select.vue 371 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/components/spec.vue 322 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/views/comment.vue 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/views/info.vue 277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/views/search-keyword.vue 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/goods/views/type.vue 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/helper/hooks/ai.ts 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/helper/hooks/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/helper/static/svg/arrow-down.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/helper/static/svg/code.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/helper/static/svg/quick.svg 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/info/views/banner.vue 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/market/views/coupon/info.vue 282 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/market/views/coupon/user.vue 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/monitor/components/site-tree.vue 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/components/discount-item.vue 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/components/goods.vue 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/components/info-item.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/components/logistics-item.vue 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/components/pay-item.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/components/remark-item.vue 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/components/user-item.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/dict/index.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/views/info.vue 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/order/views/refund.vue 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/shop/views/recycleorderitem.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/user/locales/zh-cn.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
build/cool/eps.d.ts
@@ -1,4 +1,292 @@
declare namespace Eps {
    interface AppComplainEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 用户ID
         */
        userId?: number;
        /**
         * 类型
         */
        type?: number;
        /**
         * 联系方式
         */
        contact?: string;
        /**
         * 内容
         */
        content?: string;
        /**
         * 图片
         */
        images?: any;
        /**
         * 状态 0-未处理 1-已处理
         */
        status?: number;
        /**
         * 处理人ID
         */
        handlerId?: number;
        /**
         * 备注
         */
        remark?: string;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 昵称
         */
        nickName?: string;
        /**
         * 头像
         */
        avatarUrl?: string;
        /**
         * 姓名
         */
        handlerName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface AppFeedbackEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 用户ID
         */
        userId?: number;
        /**
         * 联系方式
         */
        contact?: string;
        /**
         * 类型
         */
        type?: number;
        /**
         * 内容
         */
        content?: string;
        /**
         * 图片
         */
        images?: any;
        /**
         * 状态 0-未处理 1-已处理
         */
        status?: number;
        /**
         * 处理人ID
         */
        handlerId?: number;
        /**
         * 备注
         */
        remark?: string;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 昵称
         */
        nickName?: string;
        /**
         * 头像
         */
        avatarUrl?: string;
        /**
         * 姓名
         */
        handlerName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface AppGoodsEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 标题
         */
        title?: string;
        /**
         * 价格
         */
        price?: number;
        /**
         * 原价
         */
        originalPrice?: number;
        /**
         * 描述
         */
        description?: string;
        /**
         * 状态 0-禁用 1-启用
         */
        status?: number;
        /**
         * 排序
         */
        sort?: number;
        /**
         * 类型 0-天 1-月 2-年 3-永久
         */
        type?: number;
        /**
         * 时长
         */
        duration?: number;
        /**
         * 标签
         */
        tag?: string;
        /**
         * 标签颜色
         */
        tagColor?: string;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface AppVersionEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 名称
         */
        name?: string;
        /**
         * 版本号
         */
        version?: string;
        /**
         * 类型
         */
        type?: number;
        /**
         * 下载地址
         */
        url?: string;
        /**
         * 强制更新 0-否 1-是
         */
        forceUpdate?: number;
        /**
         * 状态 0-禁用 1-启用
         */
        status?: number;
        /**
         * 热更新 0-否 1-是
         */
        hotUpdate?: number;
        /**
         * 描述
         */
        description?: string;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface BaseSysDepartmentEntity {
        /**
         * ID
@@ -711,9 +999,29 @@
        phone?: string;
        /**
         * 密码
         */
        password?: string;
        /**
         * 性别
         */
        sex?: string;
        /**
         * 生日
         */
        birthday?: Date;
        /**
         * 入职日期
         */
        hireDate?: Date;
        /**
         * 离职日期
         */
        leaveDate?: Date;
        /**
         * 状态
@@ -920,6 +1228,120 @@
        [key: string]: any;
    }
    interface CsMsgEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 用户ID
         */
        userId?: number;
        /**
         * 会话ID
         */
        sessionId?: number;
        /**
         * 消息内容
         */
        content?: any;
        /**
         * 类型 0-反馈 1-回复
         */
        type?: number;
        /**
         * 状态 0-未读 1-已读
         */
        status?: number;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 昵称
         */
        nickName?: string;
        /**
         * 头像
         */
        avatarUrl?: string;
        /**
         * 姓名
         */
        adminUserName?: string;
        /**
         * 头像
         */
        adminUserHeadImg?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface CsSessionEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 用户ID
         */
        userId?: number;
        /**
         * 最后一条消息
         */
        lastMsg?: any;
        /**
         * 客服未读消息数
         */
        adminUnreadCount?: number;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 昵称
         */
        nickName?: string;
        /**
         * 头像
         */
        avatarUrl?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface DemoGoodsEntity {
        /**
         * ID
@@ -1064,6 +1486,457 @@
         * 更新时间
         */
        updateTime?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface GoodsCommentEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 用户ID
         */
        userId?: number;
        /**
         * 商品ID
         */
        goodsId?: number;
        /**
         * 订单ID
         */
        orderId?: number;
        /**
         * 内容
         */
        content?: string;
        /**
         * 星数
         */
        starCount?: number;
        /**
         * 图片
         */
        pics?: any;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 昵称
         */
        nickName?: string;
        /**
         * 头像
         */
        avatarUrl?: string;
        /**
         * 标题
         */
        title?: string;
        /**
         * 主图
         */
        mainPic?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface GoodsInfoEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 类型ID
         */
        typeId?: number;
        /**
         * 标题
         */
        title?: string;
        /**
         * 副标题
         */
        subTitle?: string;
        /**
         * 主图
         */
        mainPic?: string;
        /**
         * 图片
         */
        pics?: any;
        /**
         * 价格
         */
        price?: number;
        /**
         * 已售
         */
        sold?: number;
        /**
         * 详情
         */
        content?: string;
        /**
         * 状态 0-下架 1-上架
         */
        status?: number;
        /**
         * 排序
         */
        sortNum?: number;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 名称
         */
        typeName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface GoodsSearchKeywordEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 名称
         */
        name?: string;
        /**
         * 排序
         */
        sortNum?: number;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface GoodsSpecEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 商品ID
         */
        goodsId?: number;
        /**
         * 名称
         */
        name?: string;
        /**
         * 价格
         */
        price?: number;
        /**
         * 库存
         */
        stock?: number;
        /**
         * 排序
         */
        sortNum?: number;
        /**
         * 图片
         */
        images?: any;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface GoodsTypeEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 名称
         */
        name?: string;
        /**
         * 父ID
         */
        parentId?: number;
        /**
         * 排序
         */
        sortNum?: number;
        /**
         * 图片
         */
        pic?: string;
        /**
         * 状态  0-禁用 1-启用
         */
        status?: number;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface InfoBannerEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 描述
         */
        description?: string;
        /**
         * 跳转路径
         */
        path?: string;
        /**
         * 图片
         */
        pic?: string;
        /**
         * 排序
         */
        sortNum?: number;
        /**
         * 状态 1:启用 2:禁用
         */
        status?: number;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface MarketCouponInfoEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 标题
         */
        title?: string;
        /**
         * 描述
         */
        description?: string;
        /**
         * 类型 0-满减
         */
        type?: number;
        /**
         * 金额
         */
        amount?: number;
        /**
         * 数量
         */
        num?: number;
        /**
         * 已领取
         */
        receivedNum?: number;
        /**
         * 开始时间
         */
        startTime?: Date;
        /**
         * 结束时间
         */
        endTime?: Date;
        /**
         * 状态 0-禁用 1-启用
         */
        status?: number;
        /**
         * 条件
         */
        condition?: any;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface MarketCouponUserEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 用户ID
         */
        userId?: number;
        /**
         * 优惠券ID
         */
        couponId?: number;
        /**
         * 状态 0-未使用 1-已使用
         */
        status?: number;
        /**
         * 使用时间
         */
        useTime?: Date;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 昵称
         */
        nickName?: string;
        /**
         * 头像
         */
        avatarUrl?: string;
        /**
         * 任意键值
@@ -1315,6 +2188,128 @@
         * 名称
         */
        buyerName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface OrderInfoEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 用户ID
         */
        userId?: number;
        /**
         * 标题
         */
        title?: string;
        /**
         * 支付方式 0-待支付 1-微信 2-支付宝
         */
        payType?: number;
        /**
         * 支付时间
         */
        payTime?: Date;
        /**
         * 订单号
         */
        orderNum?: string;
        /**
         * 状态 0-待付款 1-待发货 2-待收货 3-待评价 4-交易完成 5-退款中 6-已退款 7-已关闭
         */
        status?: number;
        /**
         * 价格
         */
        price?: number;
        /**
         * 优惠金额
         */
        discountPrice?: number;
        /**
         * 优惠来源
         */
        discountSource?: any;
        /**
         * 地址
         */
        address?: any;
        /**
         * 物流信息
         */
        logistics?: any;
        /**
         * 退款
         */
        refund?: any;
        /**
         * 退款状态
         */
        refundStatus?: number;
        /**
         * 退款申请时间
         */
        refundApplyTime?: Date;
        /**
         * 备注
         */
        remark?: string;
        /**
         * 关闭备注
         */
        closeRemark?: string;
        /**
         * 已开票: 0-未开票 1-已开票
         */
        invoice?: number;
        /**
         * 微信类型 0-小程序 1-公众号 2-App
         */
        wxType?: number;
        /**
         * 创建时间
         */
        createTime?: Date;
        /**
         * 更新时间
         */
        updateTime?: Date;
        /**
         * 昵称
         */
        nickName?: string;
        /**
         * 头像
         */
        avatarUrl?: string;
        /**
         * 任意键值
@@ -1792,6 +2787,294 @@
         * 站点类型
         */
        siteType?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface PushSorterAlarmEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 身份证号
         */
        idCard?: string;
        /**
         * 所属单位
         */
        departmentId?: number;
        /**
         * 数据时间
         */
        uploadTime?: Date;
        /**
         * 报警类型
         */
        alarmType?: string;
        /**
         * 报警内容
         */
        content?: string;
        /**
         * 报警级别
         */
        alarmLevel?: string;
        /**
         * 处理状态
         */
        status?: number;
        /**
         * 处理人ID
         */
        handleUser?: number;
        /**
         * 处理时间
         */
        handleTime?: Date;
        /**
         * 创建时间
         */
        createTime?: string;
        /**
         * 更新时间
         */
        updateTime?: string;
        /**
         * 部门名称
         */
        departmentName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface PushSorterAttendanceEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 身份证号
         */
        idCard?: string;
        /**
         * 所属单位
         */
        departmentId?: number;
        /**
         * 数据时间
         */
        uploadTime?: Date;
        /**
         * 工作日期
         */
        workDate?: Date;
        /**
         * 签到时间
         */
        checkInTime?: Date;
        /**
         * 签退时间
         */
        checkOutTime?: Date;
        /**
         * 签到地点
         */
        checkInLocation?: string;
        /**
         * 签退地点
         */
        checkOutLocation?: string;
        /**
         * 工作时长
         */
        workHours?: number;
        /**
         * 考勤状态
         */
        status?: string;
        /**
         * 创建时间
         */
        createTime?: string;
        /**
         * 更新时间
         */
        updateTime?: string;
        /**
         * 部门名称
         */
        departmentName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface PushSorterGpsEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 身份证号
         */
        idCard?: string;
        /**
         * 姓名
         */
        name?: string;
        /**
         * 所属单位
         */
        departmentId?: number;
        /**
         * 数据时间
         */
        uploadTime?: Date;
        /**
         * 经度
         */
        longitude?: number;
        /**
         * 纬度
         */
        latitude?: number;
        /**
         * 高程
         */
        height?: number;
        /**
         * 速度
         */
        speed?: number;
        /**
         * 精度
         */
        accuracy?: number;
        /**
         * 创建时间
         */
        createTime?: string;
        /**
         * 更新时间
         */
        updateTime?: string;
        /**
         * 部门名称
         */
        departmentName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface PushSorterHealthEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 身份证号
         */
        idCard?: string;
        /**
         * 所属单位
         */
        departmentId?: number;
        /**
         * 数据时间
         */
        uploadTime?: Date;
        /**
         * 体温
         */
        temperature?: number;
        /**
         * 心率
         */
        heartRate?: number;
        /**
         * 收缩压
         */
        bloodPressureHigh?: number;
        /**
         * 舒张压
         */
        bloodPressureLow?: number;
        /**
         * 血氧饱和度
         */
        bloodOxygen?: number;
        /**
         * 创建时间
         */
        createTime?: string;
        /**
         * 更新时间
         */
        updateTime?: string;
        /**
         * 部门名称
         */
        departmentName?: string;
        /**
         * 任意键值
@@ -2708,6 +3991,438 @@
        [key: string]: any;
    }
    interface RecycleAppointmentEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 预约编号
         */
        appointmentNo?: string;
        /**
         * 学校ID
         */
        departmentId?: number;
        /**
         * 用户ID
         */
        campusUserId?: string;
        /**
         * 联系人
         */
        contactName?: string;
        /**
         * 联系电话
         */
        contactPhone?: string;
        /**
         * 详细地址
         */
        address?: string;
        /**
         * 经度
         */
        longitude?: number;
        /**
         * 纬度
         */
        latitude?: number;
        /**
         * 预约日期
         */
        appointmentDate?: string;
        /**
         * 预约时间段
         */
        appointmentSlot?: string;
        /**
         * 预计重量
         */
        expectedWeight?: number;
        /**
         * 状态
         */
        status?: string;
        /**
         * 分拣员ID
         */
        sorterId?: string;
        /**
         * 回收订单ID
         */
        recycleOrderId?: string;
        /**
         * 创建时间
         */
        createTime?: string;
        /**
         * 更新时间
         */
        updateTime?: string;
        /**
         * 部门名称
         */
        departmentName?: string;
        /**
         * 姓名
         */
        sorterName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface RecycleAppointmentLogEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 预约ID
         */
        appointmentId?: number;
        /**
         * 操作类型
         */
        action?: string;
        /**
         * 操作人类型
         */
        operatorType?: string;
        /**
         * 操作人ID
         */
        operatorId?: number;
        /**
         * 描述
         */
        remark?: string;
        /**
         * 创建时间
         */
        createTime?: string;
        /**
         * 更新时间
         */
        updateTime?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface RecycleItemEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 所属单位
         */
        departmentId?: string;
        /**
         * 分类ID
         */
        itemId?: string;
        /**
         * 名称
         */
        itemName?: string;
        /**
         * 包含品类
         */
        including?: string;
        /**
         * 单位
         */
        unit?: string;
        /**
         * 价格
         */
        price?: number;
        /**
         * 碳积分
         */
        carbonPoint?: number;
        /**
         * 废品类别编码
         */
        category?: string;
        /**
         * 状态
         */
        status?: number;
        /**
         * 创建时间
         */
        createTime?: string;
        /**
         * 更新时间
         */
        updateTime?: string;
        /**
         * 部门名称
         */
        departmentName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface RecycleOrderEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 订单编号
         */
        orderNo?: string;
        /**
         * 订单类型
         */
        orderType?: string;
        /**
         * 用户ID
         */
        campusUserId?: string;
        /**
         * 学校ID
         */
        departmentId?: number;
        /**
         * 站点ID
         */
        siteId?: string;
        /**
         * 分拣员ID
         */
        sorterId?: string;
        /**
         * 预约ID
         */
        appointmentId?: number;
        /**
         * 状态
         */
        status?: string;
        /**
         * 总重量
         */
        totalWeight?: number;
        /**
         * 总金额
         */
        totalAmount?: number;
        /**
         * 总碳积分
         */
        totalCarbonPoint?: number;
        /**
         * 完成时间
         */
        finishTime?: Date;
        /**
         * 创建时间
         */
        createTime?: string;
        /**
         * 更新时间
         */
        updateTime?: string;
        /**
         * 部门名称
         */
        departmentName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface RecycleOrderItemEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 订单NO
         */
        orderNo?: string;
        /**
         * 垃圾分类ID
         */
        itemId?: string;
        /**
         * 垃圾分类名称
         */
        itemName?: string;
        /**
         * 固废国标分类
         */
        category?: string;
        /**
         * 重量
         */
        weight?: number;
        /**
         * 单价
         */
        unitPrice?: number;
        /**
         * 金额
         */
        amount?: number;
        /**
         * 碳积分
         */
        carbonPoint?: number;
        /**
         * 创建时间
         */
        createTime?: string;
        /**
         * 更新时间
         */
        updateTime?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface RecycleTransactionEntity {
        /**
         * ID
         */
        id?: number;
        /**
         * 用户ID
         */
        userId?: string;
        /**
         * 机构ID
         */
        departmentId?: number;
        /**
         * 流水类型
         */
        type?: string;
        /**
         * 金额
         */
        amount?: number;
        /**
         * 来源类型
         */
        sourceType?: string;
        /**
         * 备注
         */
        remark?: string;
        /**
         * 订单ID
         */
        orderId?: string;
        /**
         * 创建时间
         */
        createTime?: string;
        /**
         * 更新时间
         */
        updateTime?: string;
        /**
         * 部门名称
         */
        departmentName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
    }
    interface SpaceInfoEntity {
        /**
         * ID
@@ -2998,9 +4713,59 @@
        phone?: string;
        /**
         * 生日
         */
        birthday?: Date;
        /**
         * 性别
         */
        gender?: number;
        /**
         * 角色
         */
        role?: string;
        /**
         * 学校ID
         */
        departmentId?: number;
        /**
         * 碳积分
         */
        carbonBalance?: number;
        /**
         * 学院ID
         */
        collegeId?: number;
        /**
         * 学院名称
         */
        collegeName?: string;
        /**
         * 专业ID
         */
        majorId?: number;
        /**
         * 专业名称
         */
        majorName?: string;
        /**
         * 班级ID
         */
        classId?: number;
        /**
         * 班级名称
         */
        className?: string;
        /**
         * 状态
@@ -3033,6 +4798,11 @@
        updateTime?: string;
        /**
         * 部门名称
         */
        departmentName?: string;
        /**
         * 任意键值
         */
        [key: string]: any;
@@ -3041,8 +4811,13 @@
    type json = any;
    type DictKey =
        | "upgradeType"
        | "complainType"
        | "feedbackType"
        | "ratedpayload"
        | "iottype"
        | "brand"
        | "occupation"
        | "sitetype"
        | "datachanel"
        | "vehicletype"
@@ -3060,6 +4835,26 @@
        pagination: PagePagination;
        list: T[];
        [key: string]: any;
    }
    interface AppComplainPageResponse {
        pagination: PagePagination;
        list: AppComplainEntity[];
    }
    interface AppFeedbackPageResponse {
        pagination: PagePagination;
        list: AppFeedbackEntity[];
    }
    interface AppGoodsPageResponse {
        pagination: PagePagination;
        list: AppGoodsEntity[];
    }
    interface AppVersionPageResponse {
        pagination: PagePagination;
        list: AppVersionEntity[];
    }
    interface BaseSysLogPageResponse {
@@ -3122,6 +4917,16 @@
        list: BasicdataWastetypeEntity[];
    }
    interface CsMsgPageResponse {
        pagination: PagePagination;
        list: CsMsgEntity[];
    }
    interface CsSessionPageResponse {
        pagination: PagePagination;
        list: CsSessionEntity[];
    }
    interface DemoGoodsPageResponse {
        pagination: PagePagination;
        list: DemoGoodsEntity[];
@@ -3137,6 +4942,46 @@
        list: DictTypeEntity[];
    }
    interface GoodsCommentPageResponse {
        pagination: PagePagination;
        list: GoodsCommentEntity[];
    }
    interface GoodsInfoPageResponse {
        pagination: PagePagination;
        list: GoodsInfoEntity[];
    }
    interface GoodsSearchKeywordPageResponse {
        pagination: PagePagination;
        list: GoodsSearchKeywordEntity[];
    }
    interface GoodsSpecPageResponse {
        pagination: PagePagination;
        list: GoodsSpecEntity[];
    }
    interface GoodsTypePageResponse {
        pagination: PagePagination;
        list: GoodsTypeEntity[];
    }
    interface InfoBannerPageResponse {
        pagination: PagePagination;
        list: InfoBannerEntity[];
    }
    interface MarketCouponInfoPageResponse {
        pagination: PagePagination;
        list: MarketCouponInfoEntity[];
    }
    interface MarketCouponUserPageResponse {
        pagination: PagePagination;
        list: MarketCouponUserEntity[];
    }
    interface MonitorCapturetaskPageResponse {
        pagination: PagePagination;
        list: PushCaptureTaskEntity[];
@@ -3150,6 +4995,11 @@
    interface OperationWastesalePageResponse {
        pagination: PagePagination;
        list: OperationWastesaleEntity[];
    }
    interface OrderInfoPageResponse {
        pagination: PagePagination;
        list: OrderInfoEntity[];
    }
    interface PluginInfoPageResponse {
@@ -3180,6 +5030,26 @@
    interface PushSiteweightPageResponse {
        pagination: PagePagination;
        list: PushSiteweightEntity[];
    }
    interface PushSorteralarmPageResponse {
        pagination: PagePagination;
        list: PushSorterAlarmEntity[];
    }
    interface PushSorterattendancePageResponse {
        pagination: PagePagination;
        list: PushSorterAttendanceEntity[];
    }
    interface PushSortergpsPageResponse {
        pagination: PagePagination;
        list: PushSorterGpsEntity[];
    }
    interface PushSorterhealthPageResponse {
        pagination: PagePagination;
        list: PushSorterHealthEntity[];
    }
    interface PushVehiclegpsPageResponse {
@@ -3217,6 +5087,36 @@
        list: ReportJoblogEntity[];
    }
    interface ShopRecycleappointmentPageResponse {
        pagination: PagePagination;
        list: RecycleAppointmentEntity[];
    }
    interface ShopRecycleappointmentlogPageResponse {
        pagination: PagePagination;
        list: RecycleAppointmentLogEntity[];
    }
    interface ShopRecycleitemPageResponse {
        pagination: PagePagination;
        list: RecycleItemEntity[];
    }
    interface ShopRecycleorderPageResponse {
        pagination: PagePagination;
        list: RecycleOrderEntity[];
    }
    interface ShopRecycleorderitemPageResponse {
        pagination: PagePagination;
        list: RecycleOrderItemEntity[];
    }
    interface ShopTransactionPageResponse {
        pagination: PagePagination;
        list: RecycleTransactionEntity[];
    }
    interface SpaceInfoPageResponse {
        pagination: PagePagination;
        list: SpaceInfoEntity[];
@@ -3240,6 +5140,238 @@
    interface UserInfoPageResponse {
        pagination: PagePagination;
        list: UserInfoEntity[];
    }
    interface AppComplain {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<AppComplainEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<AppComplainEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<AppComplainPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface AppFeedback {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<AppFeedbackEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<AppFeedbackEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<AppFeedbackPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface AppGoods {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<AppGoodsEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<AppGoodsEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<AppGoodsPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface AppVersion {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<AppVersionEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<AppVersionEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<AppVersionPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface BaseCoding {
@@ -4155,6 +6287,112 @@
        request: Request;
    }
    interface CountHome {
        /**
         * 商品分类
         */
        goodsCategory(data?: any): Promise<any>;
        /**
         * 订单概况
         */
        orderSummary(data?: any): Promise<any>;
        /**
         * 用户概况
         */
        userSummary(data?: any): Promise<any>;
        /**
         * 订单图表
         */
        orderChart(data?: any): Promise<any>;
        /**
         * 用户图表
         */
        userChart(data?: any): Promise<any>;
        /**
         * 商品排行
         */
        goodsRank(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            goodsCategory: string;
            orderSummary: string;
            userSummary: string;
            orderChart: string;
            userChart: string;
            goodsRank: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            goodsCategory: boolean;
            orderSummary: boolean;
            userSummary: boolean;
            orderChart: boolean;
            userChart: boolean;
            goodsRank: boolean;
        };
        request: Request;
    }
    interface CsMsg {
        /**
         * 未读消息数
         */
        unreadCount(data?: any): Promise<any>;
        /**
         * 标记已读
         */
        read(data?: any): Promise<any>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<CsMsgPageResponse>;
        /**
         * 权限标识
         */
        permission: { unreadCount: string; read: string; page: string };
        /**
         * 权限状态
         */
        _permission: { unreadCount: boolean; read: boolean; page: boolean };
        request: Request;
    }
    interface CsSession {
        /**
         * 分页查询
         */
        page(data?: any): Promise<CsSessionPageResponse>;
        /**
         * 权限标识
         */
        permission: { page: string };
        /**
         * 权限状态
         */
        _permission: { page: boolean };
        request: Request;
    }
    interface DemoGoods {
        /**
         * 删除
@@ -4339,6 +6577,431 @@
         * 分页查询
         */
        page(data?: any): Promise<DictTypePageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface GoodsComment {
        /**
         * 分页查询
         */
        page(data?: any): Promise<GoodsCommentPageResponse>;
        /**
         * 权限标识
         */
        permission: { page: string };
        /**
         * 权限状态
         */
        _permission: { page: boolean };
        request: Request;
    }
    interface GoodsInfo {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<GoodsInfoEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<GoodsInfoEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<GoodsInfoPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface GoodsSearchKeyword {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<GoodsSearchKeywordEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<GoodsSearchKeywordEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<GoodsSearchKeywordPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface GoodsSpec {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<GoodsSpecEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<GoodsSpecEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<GoodsSpecPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface GoodsType {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<GoodsTypeEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<GoodsTypeEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<GoodsTypePageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface InfoBanner {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<InfoBannerEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<InfoBannerEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<InfoBannerPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface MarketCouponInfo {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<MarketCouponInfoEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<MarketCouponInfoEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<MarketCouponInfoPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface MarketCouponUser {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<MarketCouponUserEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<MarketCouponUserEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<MarketCouponUserPageResponse>;
        /**
         * 新增
@@ -4567,6 +7230,78 @@
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface OrderInfo {
        /**
         * 退款处理
         */
        refundHandle(data?: any): Promise<any>;
        /**
         * 物流信息
         */
        logistics(data?: any): Promise<any>;
        /**
         * 发货
         */
        deliver(data?: any): Promise<any>;
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<OrderInfoEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<OrderInfoEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<OrderInfoPageResponse>;
        /**
         * 权限标识
         */
        permission: {
            refundHandle: string;
            logistics: string;
            deliver: string;
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            refundHandle: boolean;
            logistics: boolean;
            deliver: boolean;
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
        };
        request: Request;
@@ -4894,6 +7629,238 @@
         * 分页查询
         */
        page(data?: any): Promise<PushSiteweightPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface PushSorteralarm {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<PushSorterAlarmEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<PushSorterAlarmEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<PushSorteralarmPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface PushSorterattendance {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<PushSorterAttendanceEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<PushSorterAttendanceEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<PushSorterattendancePageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface PushSortergps {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<PushSorterGpsEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<PushSorterGpsEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<PushSortergpsPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface PushSorterhealth {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<PushSorterHealthEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<PushSorterHealthEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<PushSorterhealthPageResponse>;
        /**
         * 新增
@@ -5304,6 +8271,361 @@
        request: Request;
    }
    interface ShopRecycleappointment {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<RecycleAppointmentEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<RecycleAppointmentEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<ShopRecycleappointmentPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface ShopRecycleappointmentlog {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<RecycleAppointmentLogEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<RecycleAppointmentLogEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<ShopRecycleappointmentlogPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface ShopRecycleitem {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<RecycleItemEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<RecycleItemEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<ShopRecycleitemPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface ShopRecycleorder {
        /**
         * saveWithSummary
         */
        saveWithSummary(data?: any): Promise<any>;
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<RecycleOrderEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<RecycleOrderEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<ShopRecycleorderPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            saveWithSummary: string;
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            saveWithSummary: boolean;
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface ShopRecycleorderitem {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<RecycleOrderItemEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<RecycleOrderItemEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<ShopRecycleorderitemPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface ShopTransaction {
        /**
         * 删除
         */
        delete(data?: any): Promise<any>;
        /**
         * 修改
         */
        update(data?: any): Promise<any>;
        /**
         * 单个信息
         */
        info(data?: any): Promise<RecycleTransactionEntity>;
        /**
         * 列表查询
         */
        list(data?: any): Promise<RecycleTransactionEntity[]>;
        /**
         * 分页查询
         */
        page(data?: any): Promise<ShopTransactionPageResponse>;
        /**
         * 新增
         */
        add(data?: any): Promise<any>;
        /**
         * 权限标识
         */
        permission: {
            delete: string;
            update: string;
            info: string;
            list: string;
            page: string;
            add: string;
        };
        /**
         * 权限状态
         */
        _permission: {
            delete: boolean;
            update: boolean;
            info: boolean;
            list: boolean;
            page: boolean;
            add: boolean;
        };
        request: Request;
    }
    interface SpaceInfo {
        /**
         * 删除
@@ -5630,6 +8952,7 @@
    type Service = {
        request: Request;
        app: { complain: AppComplain; feedback: AppFeedback; goods: AppGoods; version: AppVersion };
        base: {
            coding: BaseCoding;
            comm: BaseComm;
@@ -5652,10 +8975,22 @@
            vehicle: BasicdataVehicle;
            wastetype: BasicdataWastetype;
        };
        count: { home: CountHome };
        cs: { msg: CsMsg; session: CsSession };
        demo: { goods: DemoGoods; tenant: DemoTenant };
        dict: { info: DictInfo; type: DictType };
        goods: {
            comment: GoodsComment;
            info: GoodsInfo;
            searchKeyword: GoodsSearchKeyword;
            spec: GoodsSpec;
            type: GoodsType;
        };
        info: { banner: InfoBanner };
        market: { coupon: { info: MarketCouponInfo; user: MarketCouponUser } };
        monitor: { capturetask: MonitorCapturetask; ys7: MonitorYs7 };
        operation: { traderecord: OperationTraderecord; wastesale: OperationWastesale };
        order: { info: OrderInfo };
        plugin: { info: PluginInfo };
        push: {
            auditlog: PushAuditlog;
@@ -5663,6 +8998,10 @@
            devicealarm: PushDevicealarm;
            deviceonoff: PushDeviceonoff;
            siteweight: PushSiteweight;
            sorteralarm: PushSorteralarm;
            sorterattendance: PushSorterattendance;
            sortergps: PushSortergps;
            sorterhealth: PushSorterhealth;
            vehiclegps: PushVehiclegps;
            weight: PushWeight;
        };
@@ -5673,6 +9012,14 @@
            dailyvehicle: ReportDailyvehicle;
            joblog: ReportJoblog;
        };
        shop: {
            recycleappointment: ShopRecycleappointment;
            recycleappointmentlog: ShopRecycleappointmentlog;
            recycleitem: ShopRecycleitem;
            recycleorder: ShopRecycleorder;
            recycleorderitem: ShopRecycleorderitem;
            transaction: ShopTransaction;
        };
        space: { info: SpaceInfo; type: SpaceType };
        task: { info: TaskInfo };
        user: { address: UserAddress; info: UserInfo };
build/cool/eps.json
@@ -1 +1 @@
[{"prefix":"/admin/base/coding","name":"","api":[{"method":"get","path":"/getModuleTree"},{"method":"post","path":"/createCode"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/comm","name":"","api":[{"method":"post","path":"/personUpdate"},{"method":"get","path":"/uploadMode"},{"method":"get","path":"/permmenu"},{"method":"get","path":"/program"},{"method":"get","path":"/person"},{"method":"post","path":"/upload"},{"method":"post","path":"/logout"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/open","name":"","api":[{"method":"get","path":"/refreshToken"},{"method":"get","path":"/captcha"},{"method":"post","path":"/login"},{"method":"get","path":"/html"},{"method":"get","path":"/eps"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/sys/department","name":"BaseSysDepartmentEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/order"},{"method":"post","path":"/list"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/sys/log","name":"BaseSysLogEntity","api":[{"method":"post","path":"/setKeep"},{"method":"get","path":"/getKeep"},{"method":"post","path":"/clear"},{"method":"post","path":"/page"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"姓名","nullable":true,"source":"b.name"},{"propertyName":"action","type":"string","length":"","comment":"行为","nullable":false,"source":"a.action"},{"propertyName":"ip","type":"string","length":"","comment":"ip","nullable":true,"source":"a.ip"}]}},{"prefix":"/admin/base/sys/menu","name":"BaseSysMenuEntity","api":[{"method":"post","path":"/create"},{"method":"post","path":"/export"},{"method":"post","path":"/import"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/parse"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/sys/param","name":"BaseSysParamEntity","api":[{"method":"get","path":"/stringbykey"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/html"},{"method":"get","path":"/info"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"dataType","type":"number","length":"","comment":"数据类型 0-字符串 1-富文本 2-文件 ","nullable":false,"defaultValue":0,"source":"a.dataType"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"名称","nullable":false,"source":"a.name"},{"propertyName":"keyName","type":"string","length":"","comment":"键","nullable":false,"source":"a.keyName"}]}},{"prefix":"/admin/base/sys/role","name":"BaseSysRoleEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"名称","nullable":false,"source":"a.name"},{"propertyName":"label","type":"string","length":"50","comment":"角色标签","nullable":true,"source":"a.label"}]}},{"prefix":"/admin/base/sys/user","name":"BaseSysUserEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/move"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/basicdata/buyer","name":"BasicdataBuyerEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"bigint","length":"","comment":"部门ID","nullable":true,"source":"a.departmentId"},{"propertyName":"name","type":"string","length":"200","comment":"名称","nullable":false,"source":"a.name"},{"propertyName":"contact","type":"string","length":"50","comment":"联系人","nullable":true,"source":"a.contact"}],"fieldLike":[{"propertyName":"name","type":"string","length":"200","comment":"名称","nullable":false,"source":"a.name"},{"propertyName":"address","type":"string","length":"255","comment":"地址","nullable":true,"source":"a.address"}],"keyWordLikeFields":[]}},{"prefix":"/admin/basicdata/driver","name":"BasicdataDriverEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"bigint","length":"","comment":"所属单位","nullable":true,"source":"a.departmentId"},{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":0,"dict":["激活","禁用"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"businessId","type":"string","length":"64","comment":"身份证号","nullable":false,"source":"a.businessId"},{"propertyName":"phone","type":"string","length":"32","comment":"手机号","nullable":true,"source":"a.phone"},{"propertyName":"licenseNo","type":"string","length":"64","comment":"驾驶证号","nullable":true,"source":"a.licenseNo"}]}},{"prefix":"/admin/basicdata/iot","name":"BasicdataIotEntity","api":[{"method":"post","path":"/getYsDevices"},{"method":"post","path":"/getSitelist"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"iotTypeCode","type":"string","length":"","comment":"终端类型","nullable":false,"source":"a.iotTypeCode"},{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":1,"dict":["禁用","启用"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"iotName","type":"string","length":"","comment":"终端名称","nullable":false,"source":"a.iotName"},{"propertyName":"iotCode","type":"string","length":"","comment":"终端编码","nullable":false,"source":"a.iotCode"}]}},{"prefix":"/admin/basicdata/site","name":"BasicdataSiteEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/basicdata/sorter","name":"BasicdataSorterEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":0,"dict":["在职","离职","禁用"],"source":"a.status"},{"propertyName":"departmentId","type":"bigint","length":"","comment":"所属单位","nullable":true,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"businessName","type":"string","length":"64","comment":"姓名","nullable":false,"source":"a.businessName"},{"propertyName":"businessId","type":"string","length":"32","comment":"身份证号","nullable":true,"source":"a.businessId"},{"propertyName":"phone","type":"string","length":"32","comment":"手机号","nullable":true,"source":"a.phone"}]}},{"prefix":"/admin/basicdata/vehicle","name":"BasicdataVehicleEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"vehicleType","type":"string","length":"","comment":"车型","nullable":false,"defaultValue":"厢式货车","dict":"vehicletype","source":"a.vehicleType"},{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":0,"dict":["启用","禁用"],"source":"a.status"},{"propertyName":"departmentId","type":"bigint","length":"","comment":"所属单位","nullable":true,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"businessId","type":"string","length":"32","comment":"车牌号","nullable":false,"source":"a.businessId"},{"propertyName":"defaultDriver","type":"bigint","length":"","comment":"默认司机","nullable":true,"source":"a.defaultDriver"}]}},{"prefix":"/admin/basicdata/wastetype","name":"BasicdataWastetypeEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"isRecyclable","type":"number","length":"","comment":"是否可回收","nullable":false,"defaultValue":0,"dict":["否","是"],"source":"a.isRecyclable"},{"propertyName":"status","type":"number","length":"","comment":"是否启用","nullable":false,"defaultValue":1,"dict":["禁用","启用"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"名称","nullable":false,"source":"a.name"},{"propertyName":"code","type":"string","length":"","comment":"代码","nullable":false,"source":"a.code"}]}},{"prefix":"/admin/demo/goods","name":"DemoGoodsEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":1,"dict":["禁用","启用"],"source":"a.status"}],"fieldLike":[{"propertyName":"title","type":"string","length":"50","comment":"标题","nullable":false,"source":"a.title"}],"keyWordLikeFields":[{"propertyName":"description","type":"string","length":"","comment":"描述","nullable":true,"source":"a.description"}]}},{"prefix":"/admin/demo/tenant","name":"DemoGoodsEntity","api":[{"method":"post","path":"/noTenant"},{"method":"post","path":"/noUse"},{"method":"post","path":"/use"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/dict/info","name":"DictInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/types"},{"method":"post","path":"/data"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/dict/type","name":"DictTypeEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/monitor/capturetask","name":"PushCaptureTaskEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"string","length":"","comment":"抓拍状态","nullable":false,"defaultValue":"PENDING","dict":["PENDING","SUCCESS","FAILED"],"source":"a.status"},{"propertyName":"captureDate","type":"string","length":"","comment":"任务执行日期","nullable":true,"source":"a.captureDate"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"deviceSerial","type":"string","length":"","comment":"设备序列号","nullable":false,"source":"a.deviceSerial"},{"propertyName":"captureTaskId","type":"string","length":"","comment":"抓图任务ID(萤石返回)","nullable":true,"source":"a.captureTaskId"}]}},{"prefix":"/admin/monitor/ys7","name":"","api":[{"method":"post","path":"/getToken"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/operation/traderecord","name":"OperationTradeRecordEntity","api":[{"method":"post","path":"/import"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"transactionType","type":"string","length":"","comment":"交易类型","nullable":true,"source":"a.transactionType"},{"propertyName":"incomeType","type":"string","length":"","comment":"收支类型","nullable":false,"defaultValue":"收入","dict":["收入","支出","其他"],"source":"a.incomeType"},{"propertyName":"payMethod","type":"string","length":"","comment":"支付方式","nullable":true,"source":"a.payMethod"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"goodsName","type":"string","length":"","comment":"商品名称","nullable":true,"source":"a.goodsName"},{"propertyName":"opponent","type":"string","length":"","comment":"交易对方","nullable":true,"source":"a.opponent"}]}},{"prefix":"/admin/operation/wastesale","name":"OperationWastesaleEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"bigint","length":"","comment":"部门ID","nullable":false,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/plugin/info","name":"PluginInfoEntity","api":[{"method":"post","path":"/install"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/push/auditlog","name":"PushAuditlogEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"deviceNo","type":"string","length":"","comment":"客户端ID","nullable":false,"source":"a.deviceNo"},{"propertyName":"pushType","type":"string","length":"","comment":"推送类型","nullable":false,"defaultValue":"weight","dict":["weight","health_gps","vehicle_gps","driver_anomaly","others"],"source":"a.pushType"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"errorMessage","type":"text","length":"","comment":"错误返回信息","nullable":true,"source":"a.errorMessage"}]}},{"prefix":"/admin/push/cameragps","name":"PushCameraGpsEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"string","length":"","comment":"定位精度","nullable":false,"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"sn","type":"string","length":"","comment":"设备序列号","nullable":false,"source":"a.sn"}]}},{"prefix":"/admin/push/devicealarm","name":"PushDevicealarmEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"devSerial","type":"string","length":"","comment":"设备序列号","nullable":false,"source":"a.devSerial"},{"propertyName":"departmentName","type":"string","length":"","comment":"部门名称","nullable":false,"source":"c.name"}]}},{"prefix":"/admin/push/deviceonoff","name":"PushDeviceOnoffEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"deviceId","type":"string","length":"","comment":"设备ID","nullable":false,"source":"a.deviceId"},{"propertyName":"subSerial","type":"string","length":"","comment":"子序列号","nullable":true,"source":"a.subSerial"},{"propertyName":"deviceName","type":"string","length":"","comment":"设备名称","nullable":true,"source":"a.deviceName"}]}},{"prefix":"/admin/push/siteweight","name":"PushSiteweightEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"deviceNo","type":"string","length":"","comment":"设备号","nullable":false,"source":"a.deviceNo"},{"propertyName":"siteType","type":"string","length":"","comment":"站点类型","nullable":false,"defaultValue":"收集点","dict":"sitetype","source":"d.siteType"}],"fieldLike":[{"propertyName":"deviceNo","type":"string","length":"","comment":"设备号","nullable":false,"source":"a.deviceNo"}],"keyWordLikeFields":[]}},{"prefix":"/admin/push/vehiclegps","name":"PushVehicleGpsEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"string","length":"","comment":"定位精度","nullable":false,"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"sn","type":"string","length":"","comment":"设备序列号","nullable":false,"source":"a.sn"},{"propertyName":"vehicleNo","type":"string","length":"","comment":"车牌号","nullable":true,"source":"a.vehicleNo"}]}},{"prefix":"/admin/push/weight","name":"PushWeightEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"deviceNo","type":"string","length":"","comment":"设备号","nullable":false,"source":"a.deviceNo"},{"propertyName":"garbageType","type":"string","length":"","comment":"垃圾类型","nullable":false,"source":"a.garbageType"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/recycle/data","name":"RecycleDataEntity","api":[{"method":"post","path":"/restore"},{"method":"get","path":"/info"},{"method":"post","path":"/page"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"userName","type":"string","length":"","comment":"姓名","nullable":true,"source":"b.name"},{"propertyName":"url","type":"string","length":"","comment":"请求的接口","nullable":true,"source":"a.url"}]}},{"prefix":"/admin/report/dailydepartment","name":"ReportDailyDepartmentEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"date","type":"date","length":"","comment":"日期","nullable":false,"source":"a.date"},{"propertyName":"departmentId","type":"number","length":"","comment":"部门ID","nullable":false,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"departmentName","type":"string","length":"","comment":"部门名称","nullable":false,"source":"a.departmentName"}]}},{"prefix":"/admin/report/dailysite","name":"ReportDailysiteEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"date","type":"date","length":"","comment":"日期","nullable":false,"source":"a.date"},{"propertyName":"siteId","type":"number","length":"","comment":"站点ID","nullable":false,"source":"a.siteId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"siteName","type":"string","length":"","comment":"站点名称","nullable":false,"source":"a.siteName"}]}},{"prefix":"/admin/report/dailyvehicle","name":"ReportDailyVehicleEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"date","type":"date","length":"","comment":"日期","nullable":false,"source":"a.date"},{"propertyName":"vehicleNo","type":"string","length":"32","comment":"车牌号","nullable":false,"source":"a.vehicleNo"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"vehicleNo","type":"string","length":"32","comment":"车牌号","nullable":false,"source":"a.vehicleNo"}]}},{"prefix":"/admin/report/joblog","name":"ReportJoblogEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"dataDate","type":"date","length":"","comment":"数据目标日期","nullable":false,"source":"a.dataDate"},{"propertyName":"status","type":"enum","length":"","comment":"状态: 未开始, 运行中, 成功, 失败","nullable":false,"defaultValue":"INIT","source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"jobName","type":"string","length":"","comment":"任务类型: SITE_DAILY(站点日汇总), DEPT_DAILY(部门日汇总)","nullable":false,"source":"a.jobName"}]}},{"prefix":"/admin/space/info","name":"SpaceInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"type","type":"string","length":"","comment":"类型","nullable":false,"source":"a.type"},{"propertyName":"classifyId","type":"number","length":"","comment":"分类ID","nullable":true,"source":"a.classifyId"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/space/type","name":"SpaceTypeEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/task/info","name":"TaskInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/start"},{"method":"post","path":"/once"},{"method":"post","path":"/stop"},{"method":"get","path":"/info"},{"method":"post","path":"/page"},{"method":"get","path":"/log"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态 0-停止 1-运行","nullable":false,"defaultValue":1,"source":"a.status"},{"propertyName":"type","type":"number","length":"","comment":"状态 0-系统 1-用户","nullable":false,"defaultValue":0,"source":"a.type"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/user/address","name":"UserAddressEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/user/info","name":"UserInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":1,"dict":["禁用","正常","已注销"],"source":"a.status"},{"propertyName":"gender","type":"number","length":"","comment":"性别","nullable":false,"defaultValue":0,"dict":["未知","男","女"],"source":"a.gender"},{"propertyName":"loginType","type":"number","length":"","comment":"登录方式","nullable":false,"defaultValue":0,"dict":["小程序","公众号","H5"],"source":"a.loginType"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"nickName","type":"string","length":"","comment":"昵称","nullable":true,"source":"a.nickName"},{"propertyName":"phone","type":"string","length":"","comment":"手机号","nullable":true,"source":"a.phone"}]}}]
[{"prefix":"/admin/app/complain","name":"AppComplainEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态 0-未处理 1-已处理","nullable":false,"defaultValue":0,"source":"a.status"},{"propertyName":"type","type":"number","length":"","comment":"类型","nullable":false,"source":"a.type"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"contact","type":"string","length":"","comment":"联系方式","nullable":false,"source":"a.contact"},{"propertyName":"nickName","type":"string","length":"","comment":"昵称","nullable":true,"source":"b.nickName"},{"propertyName":"handlerName","type":"string","length":"","comment":"姓名","nullable":true,"source":"c.name"}]}},{"prefix":"/admin/app/feedback","name":"AppFeedbackEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态 0-未处理 1-已处理","nullable":false,"defaultValue":0,"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"contact","type":"string","length":"","comment":"联系方式","nullable":false,"source":"a.contact"},{"propertyName":"nickName","type":"string","length":"","comment":"昵称","nullable":true,"source":"b.nickName"},{"propertyName":"handlerName","type":"string","length":"","comment":"姓名","nullable":true,"source":"c.name"}]}},{"prefix":"/admin/app/goods","name":"AppGoodsEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态 0-禁用 1-启用","nullable":false,"defaultValue":1,"source":"a.status"},{"propertyName":"type","type":"number","length":"","comment":"类型 0-天 1-月 2-年 3-永久","nullable":false,"defaultValue":0,"source":"a.type"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"title","type":"string","length":"","comment":"标题","nullable":false,"source":"a.title"}]}},{"prefix":"/admin/app/version","name":"AppVersionEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态 0-禁用 1-启用","nullable":false,"defaultValue":1,"source":"a.status"},{"propertyName":"type","type":"number","length":"","comment":"类型","nullable":false,"defaultValue":0,"source":"a.type"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"名称","nullable":false,"source":"a.name"},{"propertyName":"version","type":"string","length":"","comment":"版本号","nullable":false,"source":"a.version"}]}},{"prefix":"/admin/base/coding","name":"","api":[{"method":"get","path":"/getModuleTree"},{"method":"post","path":"/createCode"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/comm","name":"","api":[{"method":"post","path":"/personUpdate"},{"method":"get","path":"/uploadMode"},{"method":"get","path":"/permmenu"},{"method":"get","path":"/program"},{"method":"get","path":"/person"},{"method":"post","path":"/upload"},{"method":"post","path":"/logout"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/open","name":"","api":[{"method":"get","path":"/refreshToken"},{"method":"get","path":"/captcha"},{"method":"post","path":"/login"},{"method":"get","path":"/html"},{"method":"get","path":"/eps"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/sys/department","name":"BaseSysDepartmentEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/order"},{"method":"post","path":"/list"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/sys/log","name":"BaseSysLogEntity","api":[{"method":"post","path":"/setKeep"},{"method":"get","path":"/getKeep"},{"method":"post","path":"/clear"},{"method":"post","path":"/page"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"姓名","nullable":true,"source":"b.name"},{"propertyName":"action","type":"string","length":"","comment":"行为","nullable":false,"source":"a.action"},{"propertyName":"ip","type":"string","length":"","comment":"ip","nullable":true,"source":"a.ip"}]}},{"prefix":"/admin/base/sys/menu","name":"BaseSysMenuEntity","api":[{"method":"post","path":"/create"},{"method":"post","path":"/export"},{"method":"post","path":"/import"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/parse"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/base/sys/param","name":"BaseSysParamEntity","api":[{"method":"get","path":"/stringbykey"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/html"},{"method":"get","path":"/info"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"dataType","type":"number","length":"","comment":"数据类型 0-字符串 1-富文本 2-文件 ","nullable":false,"defaultValue":0,"source":"a.dataType"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"名称","nullable":false,"source":"a.name"},{"propertyName":"keyName","type":"string","length":"","comment":"键","nullable":false,"source":"a.keyName"}]}},{"prefix":"/admin/base/sys/role","name":"BaseSysRoleEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"名称","nullable":false,"source":"a.name"},{"propertyName":"label","type":"string","length":"50","comment":"角色标签","nullable":true,"source":"a.label"}]}},{"prefix":"/admin/base/sys/user","name":"BaseSysUserEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/move"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/basicdata/buyer","name":"BasicdataBuyerEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"bigint","length":"","comment":"部门ID","nullable":true,"source":"a.departmentId"},{"propertyName":"name","type":"string","length":"200","comment":"名称","nullable":false,"source":"a.name"},{"propertyName":"contact","type":"string","length":"50","comment":"联系人","nullable":true,"source":"a.contact"}],"fieldLike":[{"propertyName":"name","type":"string","length":"200","comment":"名称","nullable":false,"source":"a.name"},{"propertyName":"address","type":"string","length":"255","comment":"地址","nullable":true,"source":"a.address"}],"keyWordLikeFields":[]}},{"prefix":"/admin/basicdata/driver","name":"BasicdataDriverEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"bigint","length":"","comment":"所属单位","nullable":true,"source":"a.departmentId"},{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":0,"dict":["激活","禁用"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"businessId","type":"string","length":"64","comment":"身份证号","nullable":false,"source":"a.businessId"},{"propertyName":"phone","type":"string","length":"32","comment":"手机号","nullable":true,"source":"a.phone"},{"propertyName":"licenseNo","type":"string","length":"64","comment":"驾驶证号","nullable":true,"source":"a.licenseNo"}]}},{"prefix":"/admin/basicdata/iot","name":"BasicdataIotEntity","api":[{"method":"post","path":"/getYsDevices"},{"method":"post","path":"/getSitelist"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"iotTypeCode","type":"string","length":"","comment":"终端类型","nullable":false,"source":"a.iotTypeCode"},{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":1,"dict":["禁用","启用"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"iotName","type":"string","length":"","comment":"终端名称","nullable":false,"source":"a.iotName"},{"propertyName":"iotCode","type":"string","length":"","comment":"终端编码","nullable":false,"source":"a.iotCode"}]}},{"prefix":"/admin/basicdata/site","name":"BasicdataSiteEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/basicdata/sorter","name":"BasicdataSorterEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":0,"dict":["在职","离职","禁用"],"source":"a.status"},{"propertyName":"departmentId","type":"bigint","length":"","comment":"所属单位","nullable":true,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"businessName","type":"string","length":"64","comment":"姓名","nullable":false,"source":"a.businessName"},{"propertyName":"businessId","type":"string","length":"32","comment":"身份证号","nullable":true,"source":"a.businessId"},{"propertyName":"phone","type":"string","length":"32","comment":"手机号","nullable":false,"source":"a.phone"}]}},{"prefix":"/admin/basicdata/vehicle","name":"BasicdataVehicleEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"vehicleType","type":"string","length":"","comment":"车型","nullable":false,"defaultValue":"厢式货车","dict":"vehicletype","source":"a.vehicleType"},{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":0,"dict":["启用","禁用"],"source":"a.status"},{"propertyName":"departmentId","type":"bigint","length":"","comment":"所属单位","nullable":true,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"businessId","type":"string","length":"32","comment":"车牌号","nullable":false,"source":"a.businessId"},{"propertyName":"defaultDriver","type":"bigint","length":"","comment":"默认司机","nullable":true,"source":"a.defaultDriver"}]}},{"prefix":"/admin/basicdata/wastetype","name":"BasicdataWastetypeEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"isRecyclable","type":"number","length":"","comment":"是否可回收","nullable":false,"defaultValue":0,"dict":["否","是"],"source":"a.isRecyclable"},{"propertyName":"status","type":"number","length":"","comment":"是否启用","nullable":false,"defaultValue":1,"dict":["禁用","启用"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"名称","nullable":false,"source":"a.name"},{"propertyName":"code","type":"string","length":"","comment":"代码","nullable":false,"source":"a.code"}]}},{"prefix":"/admin/count/home","name":"","api":[{"method":"post","path":"/goodsCategory"},{"method":"post","path":"/orderSummary"},{"method":"post","path":"/userSummary"},{"method":"post","path":"/orderChart"},{"method":"post","path":"/userChart"},{"method":"post","path":"/goodsRank"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/cs/msg","name":"CsMsgEntity","api":[{"method":"get","path":"/unreadCount"},{"method":"post","path":"/read"},{"method":"post","path":"/page"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/cs/session","name":"CsSessionEntity","api":[{"method":"post","path":"/page"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/demo/goods","name":"DemoGoodsEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":1,"dict":["禁用","启用"],"source":"a.status"}],"fieldLike":[{"propertyName":"title","type":"string","length":"50","comment":"标题","nullable":false,"source":"a.title"}],"keyWordLikeFields":[{"propertyName":"description","type":"string","length":"","comment":"描述","nullable":true,"source":"a.description"}]}},{"prefix":"/admin/demo/tenant","name":"DemoGoodsEntity","api":[{"method":"post","path":"/noTenant"},{"method":"post","path":"/noUse"},{"method":"post","path":"/use"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/dict/info","name":"DictInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/types"},{"method":"post","path":"/data"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/dict/type","name":"DictTypeEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/goods/comment","name":"GoodsCommentEntity","api":[{"method":"post","path":"/page"}],"search":{"fieldEq":[{"propertyName":"goodsId","type":"number","length":"","comment":"商品ID","nullable":false,"source":"a.goodsId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"content","type":"string","length":"","comment":"内容","nullable":false,"source":"a.content"},{"propertyName":"nickName","type":"string","length":"","comment":"昵称","nullable":true,"source":"b.nickName"}]}},{"prefix":"/admin/goods/info","name":"GoodsInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态 0-下架 1-上架","nullable":false,"defaultValue":0,"source":"a.status"},{"propertyName":"typeId","type":"number","length":"","comment":"类型ID","nullable":false,"source":"a.typeId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"title","type":"string","length":"","comment":"标题","nullable":false,"source":"a.title"},{"propertyName":"subTitle","type":"string","length":"","comment":"副标题","nullable":true,"source":"a.subTitle"}]}},{"prefix":"/admin/goods/searchKeyword","name":"GoodsSearchKeywordEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"名称","nullable":false,"source":"a.name"}]}},{"prefix":"/admin/goods/spec","name":"GoodsSpecEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/goods/type","name":"GoodsTypeEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"parentId","type":"number","length":"","comment":"父ID","nullable":true,"source":"a.parentId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"name","type":"string","length":"","comment":"名称","nullable":false,"source":"a.name"}]}},{"prefix":"/admin/info/banner","name":"InfoBannerEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态 1:启用 2:禁用","nullable":false,"defaultValue":1,"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/market/coupon/info","name":"MarketCouponInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态 0-禁用 1-启用","nullable":false,"defaultValue":0,"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"title","type":"string","length":"","comment":"标题","nullable":false,"source":"a.title"}]}},{"prefix":"/admin/market/coupon/user","name":"MarketCouponUserEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态 0-未使用 1-已使用","nullable":false,"defaultValue":0,"source":"a.status"},{"propertyName":"couponId","type":"number","length":"","comment":"优惠券ID","nullable":false,"source":"a.couponId"},{"propertyName":"userId","type":"number","length":"","comment":"用户ID","nullable":false,"source":"a.userId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"nickName","type":"string","length":"","comment":"昵称","nullable":true,"source":"b.nickName"}]}},{"prefix":"/admin/monitor/capturetask","name":"PushCaptureTaskEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"string","length":"","comment":"抓拍状态","nullable":false,"defaultValue":"PENDING","dict":["PENDING","SUCCESS","FAILED"],"source":"a.status"},{"propertyName":"captureDate","type":"string","length":"","comment":"任务执行日期","nullable":true,"source":"a.captureDate"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"deviceSerial","type":"string","length":"","comment":"设备序列号","nullable":false,"source":"a.deviceSerial"},{"propertyName":"captureTaskId","type":"string","length":"","comment":"抓图任务ID(萤石返回)","nullable":true,"source":"a.captureTaskId"}]}},{"prefix":"/admin/monitor/ys7","name":"","api":[{"method":"post","path":"/getToken"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/operation/traderecord","name":"OperationTradeRecordEntity","api":[{"method":"post","path":"/import"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"transactionType","type":"string","length":"","comment":"交易类型","nullable":true,"source":"a.transactionType"},{"propertyName":"incomeType","type":"string","length":"","comment":"收支类型","nullable":false,"defaultValue":"收入","dict":["收入","支出","其他"],"source":"a.incomeType"},{"propertyName":"payMethod","type":"string","length":"","comment":"支付方式","nullable":true,"source":"a.payMethod"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"goodsName","type":"string","length":"","comment":"商品名称","nullable":true,"source":"a.goodsName"},{"propertyName":"opponent","type":"string","length":"","comment":"交易对方","nullable":true,"source":"a.opponent"}]}},{"prefix":"/admin/operation/wastesale","name":"OperationWastesaleEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"bigint","length":"","comment":"部门ID","nullable":false,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/order/info","name":"OrderInfoEntity","api":[{"method":"post","path":"/refundHandle"},{"method":"get","path":"/logistics"},{"method":"post","path":"/deliver"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"}],"search":{"fieldEq":[{"propertyName":"status","type":"tinyint","length":"","comment":"状态 0-待付款 1-待发货 2-待收货 3-待评价 4-交易完成 5-退款中 6-已退款 7-已关闭","nullable":false,"defaultValue":0,"source":"a.status"},{"propertyName":"payType","type":"number","length":"","comment":"支付方式 0-待支付 1-微信 2-支付宝","nullable":false,"defaultValue":0,"source":"a.payType"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"title","type":"string","length":"","comment":"标题","nullable":true,"source":"a.title"},{"propertyName":"orderNum","type":"string","length":"","comment":"订单号","nullable":true,"source":"a.orderNum"},{"propertyName":"nickName","type":"string","length":"","comment":"昵称","nullable":true,"source":"b.nickName"}]}},{"prefix":"/admin/plugin/info","name":"PluginInfoEntity","api":[{"method":"post","path":"/install"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/push/auditlog","name":"PushAuditlogEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"deviceNo","type":"string","length":"","comment":"客户端ID","nullable":false,"source":"a.deviceNo"},{"propertyName":"pushType","type":"string","length":"","comment":"推送类型","nullable":false,"defaultValue":"weight","dict":["weight","health_gps","vehicle_gps","driver_anomaly","others"],"source":"a.pushType"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"errorMessage","type":"text","length":"","comment":"错误返回信息","nullable":true,"source":"a.errorMessage"}]}},{"prefix":"/admin/push/cameragps","name":"PushCameraGpsEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"string","length":"","comment":"定位精度","nullable":false,"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"sn","type":"string","length":"","comment":"设备序列号","nullable":false,"source":"a.sn"}]}},{"prefix":"/admin/push/devicealarm","name":"PushDevicealarmEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"devSerial","type":"string","length":"","comment":"设备序列号","nullable":false,"source":"a.devSerial"},{"propertyName":"departmentName","type":"string","length":"","comment":"部门名称","nullable":false,"source":"c.name"}]}},{"prefix":"/admin/push/deviceonoff","name":"PushDeviceOnoffEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"deviceId","type":"string","length":"","comment":"设备ID","nullable":false,"source":"a.deviceId"},{"propertyName":"subSerial","type":"string","length":"","comment":"子序列号","nullable":true,"source":"a.subSerial"},{"propertyName":"deviceName","type":"string","length":"","comment":"设备名称","nullable":true,"source":"a.deviceName"}]}},{"prefix":"/admin/push/siteweight","name":"PushSiteweightEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"deviceNo","type":"string","length":"","comment":"设备号","nullable":false,"source":"a.deviceNo"},{"propertyName":"siteType","type":"string","length":"","comment":"站点类型","nullable":false,"defaultValue":"收集点","dict":"sitetype","source":"d.siteType"}],"fieldLike":[{"propertyName":"deviceNo","type":"string","length":"","comment":"设备号","nullable":false,"source":"a.deviceNo"}],"keyWordLikeFields":[]}},{"prefix":"/admin/push/sorteralarm","name":"PushSorterAlarmEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"number","length":"","comment":"所属单位","nullable":false,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/push/sorterattendance","name":"PushSorterAttendanceEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"number","length":"","comment":"所属单位","nullable":false,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/push/sortergps","name":"PushSorterGpsEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"number","length":"","comment":"所属单位","nullable":false,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/push/sorterhealth","name":"PushSorterHealthEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"departmentId","type":"number","length":"","comment":"所属单位","nullable":false,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/push/vehiclegps","name":"PushVehicleGpsEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"string","length":"","comment":"定位精度","nullable":false,"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"sn","type":"string","length":"","comment":"设备序列号","nullable":false,"source":"a.sn"},{"propertyName":"vehicleNo","type":"string","length":"","comment":"车牌号","nullable":true,"source":"a.vehicleNo"}]}},{"prefix":"/admin/push/weight","name":"PushWeightEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"deviceNo","type":"string","length":"","comment":"设备号","nullable":false,"source":"a.deviceNo"},{"propertyName":"garbageType","type":"string","length":"","comment":"垃圾类型","nullable":false,"source":"a.garbageType"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/recycle/data","name":"RecycleDataEntity","api":[{"method":"post","path":"/restore"},{"method":"get","path":"/info"},{"method":"post","path":"/page"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"userName","type":"string","length":"","comment":"姓名","nullable":true,"source":"b.name"},{"propertyName":"url","type":"string","length":"","comment":"请求的接口","nullable":true,"source":"a.url"}]}},{"prefix":"/admin/report/dailydepartment","name":"ReportDailyDepartmentEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"date","type":"date","length":"","comment":"日期","nullable":false,"source":"a.date"},{"propertyName":"departmentId","type":"number","length":"","comment":"部门ID","nullable":false,"source":"a.departmentId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"departmentName","type":"string","length":"","comment":"部门名称","nullable":false,"source":"a.departmentName"}]}},{"prefix":"/admin/report/dailysite","name":"ReportDailysiteEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"date","type":"date","length":"","comment":"日期","nullable":false,"source":"a.date"},{"propertyName":"siteId","type":"number","length":"","comment":"站点ID","nullable":false,"source":"a.siteId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"siteName","type":"string","length":"","comment":"站点名称","nullable":false,"source":"a.siteName"}]}},{"prefix":"/admin/report/dailyvehicle","name":"ReportDailyVehicleEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"date","type":"date","length":"","comment":"日期","nullable":false,"source":"a.date"},{"propertyName":"vehicleNo","type":"string","length":"32","comment":"车牌号","nullable":false,"source":"a.vehicleNo"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"vehicleNo","type":"string","length":"32","comment":"车牌号","nullable":false,"source":"a.vehicleNo"}]}},{"prefix":"/admin/report/joblog","name":"ReportJoblogEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"dataDate","type":"date","length":"","comment":"数据目标日期","nullable":false,"source":"a.dataDate"},{"propertyName":"status","type":"enum","length":"","comment":"状态: 未开始, 运行中, 成功, 失败","nullable":false,"defaultValue":"INIT","source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"jobName","type":"string","length":"","comment":"任务类型: SITE_DAILY(站点日汇总), DEPT_DAILY(部门日汇总)","nullable":false,"source":"a.jobName"}]}},{"prefix":"/admin/shop/recycleappointment","name":"RecycleAppointmentEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"string","length":"","comment":"状态","nullable":false,"defaultValue":"未知","dict":["待审核","待接单","已接单","上门中","已完成","已取消","未知"],"source":"a.status"},{"propertyName":"departmentId","type":"number","length":"","comment":"学校ID","nullable":true,"source":"a.departmentId"},{"propertyName":"sorterId","type":"string","length":"","comment":"分拣员ID","nullable":true,"defaultValue":0,"source":"a.sorterId"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"appointmentNo","type":"string","length":"","comment":"预约编号","nullable":false,"source":"a.appointmentNo"},{"propertyName":"contactName","type":"string","length":"","comment":"联系人","nullable":false,"source":"a.contactName"},{"propertyName":"contactPhone","type":"string","length":"","comment":"联系电话","nullable":false,"source":"a.contactPhone"}]}},{"prefix":"/admin/shop/recycleappointmentlog","name":"RecycleAppointmentLogEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"appointmentId","type":"number","length":"","comment":"预约ID","nullable":false,"source":"a.appointmentId"},{"propertyName":"action","type":"string","length":"","comment":"操作类型","nullable":false,"dict":["创建预约","审核通过","分配人员","接单","到达","开始回收","完成","取消","改期"],"source":"a.action"},{"propertyName":"operatorType","type":"string","length":"","comment":"操作人类型","nullable":false,"source":"a.operatorType"},{"propertyName":"operatorId","type":"number","length":"","comment":"操作人ID","nullable":false,"source":"a.operatorId"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/shop/recycleitem","name":"RecycleItemEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"itemId","type":"string","length":"","comment":"分类ID","nullable":false,"source":"a.itemId"},{"propertyName":"category","type":"string","length":"","comment":"废品类别编码","nullable":false,"source":"a.category"},{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":0,"dict":["启用","禁用"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/shop/recycleorder","name":"RecycleOrderEntity","api":[{"method":"post","path":"/saveWithSummary"},{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"orderType","type":"string","length":"","comment":"订单类型","nullable":false,"defaultValue":"现场","dict":["现场","预约","大屏","活动"],"source":"a.orderType"},{"propertyName":"status","type":"string","length":"","comment":"状态","nullable":false,"defaultValue":"已完成","dict":["待处理","进行中","已完成","已取消"],"source":"a.status"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"orderNo","type":"string","length":"","comment":"订单编号","nullable":false,"source":"a.orderNo"}]}},{"prefix":"/admin/shop/recycleorderitem","name":"RecycleOrderItemEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"orderNo","type":"string","length":"","comment":"订单NO","nullable":false,"source":"a.orderNo"},{"propertyName":"itemId","type":"string","length":"","comment":"垃圾分类ID","nullable":false,"source":"a.itemId"}],"fieldLike":[{"propertyName":"itemName","type":"string","length":"","comment":"垃圾分类名称","nullable":false,"source":"a.itemName"}],"keyWordLikeFields":[]}},{"prefix":"/admin/shop/transaction","name":"RecycleTransactionEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"remark","type":"string","length":"","comment":"备注","nullable":true,"source":"a.remark"}]}},{"prefix":"/admin/space/info","name":"SpaceInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"type","type":"string","length":"","comment":"类型","nullable":false,"source":"a.type"},{"propertyName":"classifyId","type":"number","length":"","comment":"分类ID","nullable":true,"source":"a.classifyId"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/space/type","name":"SpaceTypeEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/task/info","name":"TaskInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"post","path":"/start"},{"method":"post","path":"/once"},{"method":"post","path":"/stop"},{"method":"get","path":"/info"},{"method":"post","path":"/page"},{"method":"get","path":"/log"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态 0-停止 1-运行","nullable":false,"defaultValue":1,"source":"a.status"},{"propertyName":"type","type":"number","length":"","comment":"状态 0-系统 1-用户","nullable":false,"defaultValue":0,"source":"a.type"}],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/user/address","name":"UserAddressEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[],"fieldLike":[],"keyWordLikeFields":[]}},{"prefix":"/admin/user/info","name":"UserInfoEntity","api":[{"method":"post","path":"/delete"},{"method":"post","path":"/update"},{"method":"get","path":"/info"},{"method":"post","path":"/list"},{"method":"post","path":"/page"},{"method":"post","path":"/add"}],"search":{"fieldEq":[{"propertyName":"status","type":"number","length":"","comment":"状态","nullable":false,"defaultValue":1,"dict":["禁用","正常","已注销"],"source":"a.status"},{"propertyName":"gender","type":"number","length":"","comment":"性别","nullable":false,"defaultValue":0,"dict":["未知","男","女"],"source":"a.gender"},{"propertyName":"loginType","type":"number","length":"","comment":"登录方式","nullable":false,"defaultValue":0,"dict":["小程序","公众号","H5"],"source":"a.loginType"}],"fieldLike":[],"keyWordLikeFields":[{"propertyName":"nickName","type":"string","length":"","comment":"昵称","nullable":true,"source":"a.nickName"},{"propertyName":"phone","type":"string","length":"","comment":"手机号","nullable":true,"source":"a.phone"}]}}]
package-lock.json
New file
Diff too large
package.json
@@ -31,6 +31,7 @@
        "mitt": "^3.0.1",
        "nprogress": "^0.2.0",
        "pinia": "^2.3.1",
        "socket.io-client": "^4.7.2",
        "store": "^2.0.12",
        "vue": "^3.5.13",
        "vue-echarts": "^7.0.3",
packages/crud/package.json
@@ -1,6 +1,6 @@
{
    "name": "@cool-vue/crud",
    "version": "8.0.6",
    "version": "8.0.2",
    "private": false,
    "main": "./dist/index.umd.js",
    "module": "./dist/index.es.js",
@@ -13,7 +13,7 @@
    },
    "dependencies": {
        "@vue/runtime-core": "^3.5.13",
        "element-plus": "^2.10.4",
        "element-plus": "^2.9.4",
        "lodash-es": "^4.17.21",
        "vue": "^3.5.13"
    },
packages/vite-plugin/dist/eps/index.d.ts
@@ -1,6 +1,6 @@
import type { Eps } from "../../types";
/**
 * 主入口:创建 eps 相关文件和 service
 * 主入口:创建 eps 相关文件和 service
 */
export declare function createEps(): Promise<{
    service: {};
packages/vite-plugin/dist/utils/index.d.ts
@@ -10,9 +10,9 @@
export declare function error(message: string): void;
export declare function success(message: string): void;
/**
 * 比较两个版本号
 * @param version1 版本号1 (如: "1.2.3")
 * @param version2 版本号2 (如: "1.2.4")
 * @returns 1: version1 > version2, 0: 相等, -1: version1 < version2
 * 比较两个版本号
 * @param version1 版本号1 (如: "1.2.3")
 * @param version2 版本号2 (如: "1.2.4")
 * @returns 1: version1 > version2, 0: 相等, -1: version1 < version2
 */
export declare function compareVersion(version1: string, version2: string): number;
packages/vite-plugin/package.json
@@ -1,6 +1,6 @@
{
    "name": "@cool-vue/vite-plugin",
    "version": "8.2.20",
    "version": "8.0.3",
    "description": "cool-admin、cool-uni builder",
    "types": "./dist/index.d.ts",
    "main": "/dist/index.js",
@@ -23,7 +23,6 @@
        "@typescript-eslint/eslint-plugin": "^7.7.1",
        "@typescript-eslint/parser": "^7.7.1",
        "eslint": "^9.1.1",
        "postcss-value-parser": "^4.2.0",
        "rollup": "^4.16.2",
        "tslib": "^2.6.2",
        "typescript": "^5.4.5",
packages/vite-plugin/pnpm-lock.yaml
New file
@@ -0,0 +1,2269 @@
lockfileVersion: '9.0'
settings:
  autoInstallPeers: true
  excludeLinksFromLockfile: false
importers:
  .:
    dependencies:
      '@vue/compiler-sfc':
        specifier: ^3.5.13
        version: 3.5.13
      axios:
        specifier: ^1.6.8
        version: 1.7.9
      glob:
        specifier: ^10.3.12
        version: 10.4.5
      lodash:
        specifier: ^4.17.21
        version: 4.17.21
      magic-string:
        specifier: ^0.30.17
        version: 0.30.17
      prettier:
        specifier: ^3.4.2
        version: 3.5.1
      svgo:
        specifier: ^3.3.2
        version: 3.3.2
    devDependencies:
      '@rollup/plugin-typescript':
        specifier: ^11.1.6
        version: 11.1.6(rollup@4.34.8)(tslib@2.8.1)(typescript@5.7.3)
      '@types/lodash':
        specifier: ^4.17.15
        version: 4.17.15
      '@types/node':
        specifier: ^20.12.7
        version: 20.17.19
      '@typescript-eslint/eslint-plugin':
        specifier: ^7.7.1
        version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3)
      '@typescript-eslint/parser':
        specifier: ^7.7.1
        version: 7.18.0(eslint@9.20.1)(typescript@5.7.3)
      eslint:
        specifier: ^9.1.1
        version: 9.20.1
      rollup:
        specifier: ^4.16.2
        version: 4.34.8
      tslib:
        specifier: ^2.6.2
        version: 2.8.1
      typescript:
        specifier: ^5.4.5
        version: 5.7.3
      vite:
        specifier: ^5.4.14
        version: 5.4.14(@types/node@20.17.19)
packages:
  '@babel/helper-string-parser@7.25.9':
    resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
    engines: {node: '>=6.9.0'}
  '@babel/helper-validator-identifier@7.25.9':
    resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
    engines: {node: '>=6.9.0'}
  '@babel/parser@7.26.9':
    resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==}
    engines: {node: '>=6.0.0'}
    hasBin: true
  '@babel/types@7.26.9':
    resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==}
    engines: {node: '>=6.9.0'}
  '@esbuild/aix-ppc64@0.21.5':
    resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
    engines: {node: '>=12'}
    cpu: [ppc64]
    os: [aix]
  '@esbuild/android-arm64@0.21.5':
    resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [android]
  '@esbuild/android-arm@0.21.5':
    resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
    engines: {node: '>=12'}
    cpu: [arm]
    os: [android]
  '@esbuild/android-x64@0.21.5':
    resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [android]
  '@esbuild/darwin-arm64@0.21.5':
    resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [darwin]
  '@esbuild/darwin-x64@0.21.5':
    resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [darwin]
  '@esbuild/freebsd-arm64@0.21.5':
    resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [freebsd]
  '@esbuild/freebsd-x64@0.21.5':
    resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [freebsd]
  '@esbuild/linux-arm64@0.21.5':
    resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [linux]
  '@esbuild/linux-arm@0.21.5':
    resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
    engines: {node: '>=12'}
    cpu: [arm]
    os: [linux]
  '@esbuild/linux-ia32@0.21.5':
    resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
    engines: {node: '>=12'}
    cpu: [ia32]
    os: [linux]
  '@esbuild/linux-loong64@0.21.5':
    resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
    engines: {node: '>=12'}
    cpu: [loong64]
    os: [linux]
  '@esbuild/linux-mips64el@0.21.5':
    resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
    engines: {node: '>=12'}
    cpu: [mips64el]
    os: [linux]
  '@esbuild/linux-ppc64@0.21.5':
    resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
    engines: {node: '>=12'}
    cpu: [ppc64]
    os: [linux]
  '@esbuild/linux-riscv64@0.21.5':
    resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
    engines: {node: '>=12'}
    cpu: [riscv64]
    os: [linux]
  '@esbuild/linux-s390x@0.21.5':
    resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
    engines: {node: '>=12'}
    cpu: [s390x]
    os: [linux]
  '@esbuild/linux-x64@0.21.5':
    resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [linux]
  '@esbuild/netbsd-x64@0.21.5':
    resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [netbsd]
  '@esbuild/openbsd-x64@0.21.5':
    resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [openbsd]
  '@esbuild/sunos-x64@0.21.5':
    resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [sunos]
  '@esbuild/win32-arm64@0.21.5':
    resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [win32]
  '@esbuild/win32-ia32@0.21.5':
    resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
    engines: {node: '>=12'}
    cpu: [ia32]
    os: [win32]
  '@esbuild/win32-x64@0.21.5':
    resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [win32]
  '@eslint-community/eslint-utils@4.4.1':
    resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==}
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
    peerDependencies:
      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
  '@eslint-community/regexpp@4.12.1':
    resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
  '@eslint/config-array@0.19.2':
    resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
  '@eslint/core@0.10.0':
    resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
  '@eslint/core@0.11.0':
    resolution: {integrity: sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
  '@eslint/eslintrc@3.2.0':
    resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
  '@eslint/js@9.20.0':
    resolution: {integrity: sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
  '@eslint/object-schema@2.1.6':
    resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
  '@eslint/plugin-kit@0.2.5':
    resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
  '@humanfs/core@0.19.1':
    resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
    engines: {node: '>=18.18.0'}
  '@humanfs/node@0.16.6':
    resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
    engines: {node: '>=18.18.0'}
  '@humanwhocodes/module-importer@1.0.1':
    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
    engines: {node: '>=12.22'}
  '@humanwhocodes/retry@0.3.1':
    resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
    engines: {node: '>=18.18'}
  '@humanwhocodes/retry@0.4.1':
    resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==}
    engines: {node: '>=18.18'}
  '@isaacs/cliui@8.0.2':
    resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
    engines: {node: '>=12'}
  '@jridgewell/sourcemap-codec@1.5.0':
    resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
  '@nodelib/fs.scandir@2.1.5':
    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
    engines: {node: '>= 8'}
  '@nodelib/fs.stat@2.0.5':
    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
    engines: {node: '>= 8'}
  '@nodelib/fs.walk@1.2.8':
    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
    engines: {node: '>= 8'}
  '@pkgjs/parseargs@0.11.0':
    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
    engines: {node: '>=14'}
  '@rollup/plugin-typescript@11.1.6':
    resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==}
    engines: {node: '>=14.0.0'}
    peerDependencies:
      rollup: ^2.14.0||^3.0.0||^4.0.0
      tslib: '*'
      typescript: '>=3.7.0'
    peerDependenciesMeta:
      rollup:
        optional: true
      tslib:
        optional: true
  '@rollup/pluginutils@5.1.4':
    resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==}
    engines: {node: '>=14.0.0'}
    peerDependencies:
      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
    peerDependenciesMeta:
      rollup:
        optional: true
  '@rollup/rollup-android-arm-eabi@4.34.8':
    resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==}
    cpu: [arm]
    os: [android]
  '@rollup/rollup-android-arm64@4.34.8':
    resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==}
    cpu: [arm64]
    os: [android]
  '@rollup/rollup-darwin-arm64@4.34.8':
    resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==}
    cpu: [arm64]
    os: [darwin]
  '@rollup/rollup-darwin-x64@4.34.8':
    resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==}
    cpu: [x64]
    os: [darwin]
  '@rollup/rollup-freebsd-arm64@4.34.8':
    resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==}
    cpu: [arm64]
    os: [freebsd]
  '@rollup/rollup-freebsd-x64@4.34.8':
    resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==}
    cpu: [x64]
    os: [freebsd]
  '@rollup/rollup-linux-arm-gnueabihf@4.34.8':
    resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==}
    cpu: [arm]
    os: [linux]
    libc: [glibc]
  '@rollup/rollup-linux-arm-musleabihf@4.34.8':
    resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==}
    cpu: [arm]
    os: [linux]
    libc: [musl]
  '@rollup/rollup-linux-arm64-gnu@4.34.8':
    resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==}
    cpu: [arm64]
    os: [linux]
    libc: [glibc]
  '@rollup/rollup-linux-arm64-musl@4.34.8':
    resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==}
    cpu: [arm64]
    os: [linux]
    libc: [musl]
  '@rollup/rollup-linux-loongarch64-gnu@4.34.8':
    resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==}
    cpu: [loong64]
    os: [linux]
    libc: [glibc]
  '@rollup/rollup-linux-powerpc64le-gnu@4.34.8':
    resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==}
    cpu: [ppc64]
    os: [linux]
    libc: [glibc]
  '@rollup/rollup-linux-riscv64-gnu@4.34.8':
    resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==}
    cpu: [riscv64]
    os: [linux]
    libc: [glibc]
  '@rollup/rollup-linux-s390x-gnu@4.34.8':
    resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==}
    cpu: [s390x]
    os: [linux]
    libc: [glibc]
  '@rollup/rollup-linux-x64-gnu@4.34.8':
    resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==}
    cpu: [x64]
    os: [linux]
    libc: [glibc]
  '@rollup/rollup-linux-x64-musl@4.34.8':
    resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==}
    cpu: [x64]
    os: [linux]
    libc: [musl]
  '@rollup/rollup-win32-arm64-msvc@4.34.8':
    resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==}
    cpu: [arm64]
    os: [win32]
  '@rollup/rollup-win32-ia32-msvc@4.34.8':
    resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==}
    cpu: [ia32]
    os: [win32]
  '@rollup/rollup-win32-x64-msvc@4.34.8':
    resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==}
    cpu: [x64]
    os: [win32]
  '@trysound/sax@0.2.0':
    resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
    engines: {node: '>=10.13.0'}
  '@types/estree@1.0.6':
    resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
  '@types/json-schema@7.0.15':
    resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
  '@types/lodash@4.17.15':
    resolution: {integrity: sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==}
  '@types/node@20.17.19':
    resolution: {integrity: sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==}
  '@typescript-eslint/eslint-plugin@7.18.0':
    resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==}
    engines: {node: ^18.18.0 || >=20.0.0}
    peerDependencies:
      '@typescript-eslint/parser': ^7.0.0
      eslint: ^8.56.0
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true
  '@typescript-eslint/parser@7.18.0':
    resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==}
    engines: {node: ^18.18.0 || >=20.0.0}
    peerDependencies:
      eslint: ^8.56.0
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true
  '@typescript-eslint/scope-manager@7.18.0':
    resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==}
    engines: {node: ^18.18.0 || >=20.0.0}
  '@typescript-eslint/type-utils@7.18.0':
    resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==}
    engines: {node: ^18.18.0 || >=20.0.0}
    peerDependencies:
      eslint: ^8.56.0
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true
  '@typescript-eslint/types@7.18.0':
    resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==}
    engines: {node: ^18.18.0 || >=20.0.0}
  '@typescript-eslint/typescript-estree@7.18.0':
    resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==}
    engines: {node: ^18.18.0 || >=20.0.0}
    peerDependencies:
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true
  '@typescript-eslint/utils@7.18.0':
    resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==}
    engines: {node: ^18.18.0 || >=20.0.0}
    peerDependencies:
      eslint: ^8.56.0
  '@typescript-eslint/visitor-keys@7.18.0':
    resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==}
    engines: {node: ^18.18.0 || >=20.0.0}
  '@vue/compiler-core@3.5.13':
    resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==}
  '@vue/compiler-dom@3.5.13':
    resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==}
  '@vue/compiler-sfc@3.5.13':
    resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==}
  '@vue/compiler-ssr@3.5.13':
    resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==}
  '@vue/shared@3.5.13':
    resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
  acorn-jsx@5.3.2:
    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
    peerDependencies:
      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
  acorn@8.14.0:
    resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==}
    engines: {node: '>=0.4.0'}
    hasBin: true
  ajv@6.12.6:
    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
  ansi-regex@5.0.1:
    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
    engines: {node: '>=8'}
  ansi-regex@6.1.0:
    resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
    engines: {node: '>=12'}
  ansi-styles@4.3.0:
    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
    engines: {node: '>=8'}
  ansi-styles@6.2.1:
    resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
    engines: {node: '>=12'}
  argparse@2.0.1:
    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
  array-union@2.1.0:
    resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
    engines: {node: '>=8'}
  asynckit@0.4.0:
    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
  axios@1.7.9:
    resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==}
  balanced-match@1.0.2:
    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
  boolbase@1.0.0:
    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
  brace-expansion@1.1.11:
    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
  brace-expansion@2.0.1:
    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
  braces@3.0.3:
    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
    engines: {node: '>=8'}
  call-bind-apply-helpers@1.0.2:
    resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
    engines: {node: '>= 0.4'}
  callsites@3.1.0:
    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
    engines: {node: '>=6'}
  chalk@4.1.2:
    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
    engines: {node: '>=10'}
  color-convert@2.0.1:
    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
    engines: {node: '>=7.0.0'}
  color-name@1.1.4:
    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
  combined-stream@1.0.8:
    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
    engines: {node: '>= 0.8'}
  commander@7.2.0:
    resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
    engines: {node: '>= 10'}
  concat-map@0.0.1:
    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
  cross-spawn@7.0.6:
    resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
    engines: {node: '>= 8'}
  css-select@5.1.0:
    resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
  css-tree@2.2.1:
    resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==}
    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
  css-tree@2.3.1:
    resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
  css-what@6.1.0:
    resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
    engines: {node: '>= 6'}
  csso@5.0.5:
    resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
  debug@4.4.0:
    resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
    engines: {node: '>=6.0'}
    peerDependencies:
      supports-color: '*'
    peerDependenciesMeta:
      supports-color:
        optional: true
  deep-is@0.1.4:
    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
  delayed-stream@1.0.0:
    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
    engines: {node: '>=0.4.0'}
  dir-glob@3.0.1:
    resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
    engines: {node: '>=8'}
  dom-serializer@2.0.0:
    resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
  domelementtype@2.3.0:
    resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
  domhandler@5.0.3:
    resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
    engines: {node: '>= 4'}
  domutils@3.2.2:
    resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
  dunder-proto@1.0.1:
    resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
    engines: {node: '>= 0.4'}
  eastasianwidth@0.2.0:
    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
  emoji-regex@8.0.0:
    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
  emoji-regex@9.2.2:
    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
  entities@4.5.0:
    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
    engines: {node: '>=0.12'}
  es-define-property@1.0.1:
    resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
    engines: {node: '>= 0.4'}
  es-errors@1.3.0:
    resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
    engines: {node: '>= 0.4'}
  es-object-atoms@1.1.1:
    resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
    engines: {node: '>= 0.4'}
  es-set-tostringtag@2.1.0:
    resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
    engines: {node: '>= 0.4'}
  esbuild@0.21.5:
    resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
    engines: {node: '>=12'}
    hasBin: true
  escape-string-regexp@4.0.0:
    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
    engines: {node: '>=10'}
  eslint-scope@8.2.0:
    resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
  eslint-visitor-keys@3.4.3:
    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
  eslint-visitor-keys@4.2.0:
    resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
  eslint@9.20.1:
    resolution: {integrity: sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
    hasBin: true
    peerDependencies:
      jiti: '*'
    peerDependenciesMeta:
      jiti:
        optional: true
  espree@10.3.0:
    resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
  esquery@1.6.0:
    resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
    engines: {node: '>=0.10'}
  esrecurse@4.3.0:
    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
    engines: {node: '>=4.0'}
  estraverse@5.3.0:
    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
    engines: {node: '>=4.0'}
  estree-walker@2.0.2:
    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
  esutils@2.0.3:
    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
    engines: {node: '>=0.10.0'}
  fast-deep-equal@3.1.3:
    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
  fast-glob@3.3.3:
    resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
    engines: {node: '>=8.6.0'}
  fast-json-stable-stringify@2.1.0:
    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
  fast-levenshtein@2.0.6:
    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
  fastq@1.19.0:
    resolution: {integrity: sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==}
  file-entry-cache@8.0.0:
    resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
    engines: {node: '>=16.0.0'}
  fill-range@7.1.1:
    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
    engines: {node: '>=8'}
  find-up@5.0.0:
    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
    engines: {node: '>=10'}
  flat-cache@4.0.1:
    resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
    engines: {node: '>=16'}
  flatted@3.3.2:
    resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==}
  follow-redirects@1.15.9:
    resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
    engines: {node: '>=4.0'}
    peerDependencies:
      debug: '*'
    peerDependenciesMeta:
      debug:
        optional: true
  foreground-child@3.3.0:
    resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
    engines: {node: '>=14'}
  form-data@4.0.2:
    resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
    engines: {node: '>= 6'}
  fsevents@2.3.3:
    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
    os: [darwin]
  function-bind@1.1.2:
    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
  get-intrinsic@1.2.7:
    resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==}
    engines: {node: '>= 0.4'}
  get-proto@1.0.1:
    resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
    engines: {node: '>= 0.4'}
  glob-parent@5.1.2:
    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
    engines: {node: '>= 6'}
  glob-parent@6.0.2:
    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
    engines: {node: '>=10.13.0'}
  glob@10.4.5:
    resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
    hasBin: true
  globals@14.0.0:
    resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
    engines: {node: '>=18'}
  globby@11.1.0:
    resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
    engines: {node: '>=10'}
  gopd@1.2.0:
    resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
    engines: {node: '>= 0.4'}
  graphemer@1.4.0:
    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
  has-flag@4.0.0:
    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
    engines: {node: '>=8'}
  has-symbols@1.1.0:
    resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
    engines: {node: '>= 0.4'}
  has-tostringtag@1.0.2:
    resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
    engines: {node: '>= 0.4'}
  hasown@2.0.2:
    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
    engines: {node: '>= 0.4'}
  ignore@5.3.2:
    resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
    engines: {node: '>= 4'}
  import-fresh@3.3.1:
    resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
    engines: {node: '>=6'}
  imurmurhash@0.1.4:
    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
    engines: {node: '>=0.8.19'}
  is-core-module@2.16.1:
    resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
    engines: {node: '>= 0.4'}
  is-extglob@2.1.1:
    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
    engines: {node: '>=0.10.0'}
  is-fullwidth-code-point@3.0.0:
    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
    engines: {node: '>=8'}
  is-glob@4.0.3:
    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
    engines: {node: '>=0.10.0'}
  is-number@7.0.0:
    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
    engines: {node: '>=0.12.0'}
  isexe@2.0.0:
    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
  jackspeak@3.4.3:
    resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
  js-yaml@4.1.0:
    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
    hasBin: true
  json-buffer@3.0.1:
    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
  json-schema-traverse@0.4.1:
    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
  json-stable-stringify-without-jsonify@1.0.1:
    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
  keyv@4.5.4:
    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
  levn@0.4.1:
    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
    engines: {node: '>= 0.8.0'}
  locate-path@6.0.0:
    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
    engines: {node: '>=10'}
  lodash.merge@4.6.2:
    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
  lodash@4.17.21:
    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
  lru-cache@10.4.3:
    resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
  magic-string@0.30.17:
    resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
  math-intrinsics@1.1.0:
    resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
    engines: {node: '>= 0.4'}
  mdn-data@2.0.28:
    resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
  mdn-data@2.0.30:
    resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
  merge2@1.4.1:
    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
    engines: {node: '>= 8'}
  micromatch@4.0.8:
    resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
    engines: {node: '>=8.6'}
  mime-db@1.52.0:
    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
    engines: {node: '>= 0.6'}
  mime-types@2.1.35:
    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
    engines: {node: '>= 0.6'}
  minimatch@3.1.2:
    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
  minimatch@9.0.5:
    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
    engines: {node: '>=16 || 14 >=14.17'}
  minipass@7.1.2:
    resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
    engines: {node: '>=16 || 14 >=14.17'}
  ms@2.1.3:
    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
  nanoid@3.3.8:
    resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
    hasBin: true
  natural-compare@1.4.0:
    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
  nth-check@2.1.1:
    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
  optionator@0.9.4:
    resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
    engines: {node: '>= 0.8.0'}
  p-limit@3.1.0:
    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
    engines: {node: '>=10'}
  p-locate@5.0.0:
    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
    engines: {node: '>=10'}
  package-json-from-dist@1.0.1:
    resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
  parent-module@1.0.1:
    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
    engines: {node: '>=6'}
  path-exists@4.0.0:
    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
    engines: {node: '>=8'}
  path-key@3.1.1:
    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
    engines: {node: '>=8'}
  path-parse@1.0.7:
    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
  path-scurry@1.11.1:
    resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
    engines: {node: '>=16 || 14 >=14.18'}
  path-type@4.0.0:
    resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
    engines: {node: '>=8'}
  picocolors@1.1.1:
    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
  picomatch@2.3.1:
    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
    engines: {node: '>=8.6'}
  picomatch@4.0.2:
    resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
    engines: {node: '>=12'}
  postcss@8.5.2:
    resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==}
    engines: {node: ^10 || ^12 || >=14}
  prelude-ls@1.2.1:
    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
    engines: {node: '>= 0.8.0'}
  prettier@3.5.1:
    resolution: {integrity: sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==}
    engines: {node: '>=14'}
    hasBin: true
  proxy-from-env@1.1.0:
    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
  punycode@2.3.1:
    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
    engines: {node: '>=6'}
  queue-microtask@1.2.3:
    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
  resolve-from@4.0.0:
    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
    engines: {node: '>=4'}
  resolve@1.22.10:
    resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
    engines: {node: '>= 0.4'}
    hasBin: true
  reusify@1.0.4:
    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
  rollup@4.34.8:
    resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==}
    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
    hasBin: true
  run-parallel@1.2.0:
    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
  semver@7.7.1:
    resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
    engines: {node: '>=10'}
    hasBin: true
  shebang-command@2.0.0:
    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
    engines: {node: '>=8'}
  shebang-regex@3.0.0:
    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
    engines: {node: '>=8'}
  signal-exit@4.1.0:
    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
    engines: {node: '>=14'}
  slash@3.0.0:
    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
    engines: {node: '>=8'}
  source-map-js@1.2.1:
    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
    engines: {node: '>=0.10.0'}
  string-width@4.2.3:
    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
    engines: {node: '>=8'}
  string-width@5.1.2:
    resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
    engines: {node: '>=12'}
  strip-ansi@6.0.1:
    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
    engines: {node: '>=8'}
  strip-ansi@7.1.0:
    resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
    engines: {node: '>=12'}
  strip-json-comments@3.1.1:
    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
    engines: {node: '>=8'}
  supports-color@7.2.0:
    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
    engines: {node: '>=8'}
  supports-preserve-symlinks-flag@1.0.0:
    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
    engines: {node: '>= 0.4'}
  svgo@3.3.2:
    resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==}
    engines: {node: '>=14.0.0'}
    hasBin: true
  to-regex-range@5.0.1:
    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
    engines: {node: '>=8.0'}
  ts-api-utils@1.4.3:
    resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==}
    engines: {node: '>=16'}
    peerDependencies:
      typescript: '>=4.2.0'
  tslib@2.8.1:
    resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
  type-check@0.4.0:
    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
    engines: {node: '>= 0.8.0'}
  typescript@5.7.3:
    resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==}
    engines: {node: '>=14.17'}
    hasBin: true
  undici-types@6.19.8:
    resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
  uri-js@4.4.1:
    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
  vite@5.4.14:
    resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==}
    engines: {node: ^18.0.0 || >=20.0.0}
    hasBin: true
    peerDependencies:
      '@types/node': ^18.0.0 || >=20.0.0
      less: '*'
      lightningcss: ^1.21.0
      sass: '*'
      sass-embedded: '*'
      stylus: '*'
      sugarss: '*'
      terser: ^5.4.0
    peerDependenciesMeta:
      '@types/node':
        optional: true
      less:
        optional: true
      lightningcss:
        optional: true
      sass:
        optional: true
      sass-embedded:
        optional: true
      stylus:
        optional: true
      sugarss:
        optional: true
      terser:
        optional: true
  which@2.0.2:
    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
    engines: {node: '>= 8'}
    hasBin: true
  word-wrap@1.2.5:
    resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
    engines: {node: '>=0.10.0'}
  wrap-ansi@7.0.0:
    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
    engines: {node: '>=10'}
  wrap-ansi@8.1.0:
    resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
    engines: {node: '>=12'}
  yocto-queue@0.1.0:
    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
    engines: {node: '>=10'}
snapshots:
  '@babel/helper-string-parser@7.25.9': {}
  '@babel/helper-validator-identifier@7.25.9': {}
  '@babel/parser@7.26.9':
    dependencies:
      '@babel/types': 7.26.9
  '@babel/types@7.26.9':
    dependencies:
      '@babel/helper-string-parser': 7.25.9
      '@babel/helper-validator-identifier': 7.25.9
  '@esbuild/aix-ppc64@0.21.5':
    optional: true
  '@esbuild/android-arm64@0.21.5':
    optional: true
  '@esbuild/android-arm@0.21.5':
    optional: true
  '@esbuild/android-x64@0.21.5':
    optional: true
  '@esbuild/darwin-arm64@0.21.5':
    optional: true
  '@esbuild/darwin-x64@0.21.5':
    optional: true
  '@esbuild/freebsd-arm64@0.21.5':
    optional: true
  '@esbuild/freebsd-x64@0.21.5':
    optional: true
  '@esbuild/linux-arm64@0.21.5':
    optional: true
  '@esbuild/linux-arm@0.21.5':
    optional: true
  '@esbuild/linux-ia32@0.21.5':
    optional: true
  '@esbuild/linux-loong64@0.21.5':
    optional: true
  '@esbuild/linux-mips64el@0.21.5':
    optional: true
  '@esbuild/linux-ppc64@0.21.5':
    optional: true
  '@esbuild/linux-riscv64@0.21.5':
    optional: true
  '@esbuild/linux-s390x@0.21.5':
    optional: true
  '@esbuild/linux-x64@0.21.5':
    optional: true
  '@esbuild/netbsd-x64@0.21.5':
    optional: true
  '@esbuild/openbsd-x64@0.21.5':
    optional: true
  '@esbuild/sunos-x64@0.21.5':
    optional: true
  '@esbuild/win32-arm64@0.21.5':
    optional: true
  '@esbuild/win32-ia32@0.21.5':
    optional: true
  '@esbuild/win32-x64@0.21.5':
    optional: true
  '@eslint-community/eslint-utils@4.4.1(eslint@9.20.1)':
    dependencies:
      eslint: 9.20.1
      eslint-visitor-keys: 3.4.3
  '@eslint-community/regexpp@4.12.1': {}
  '@eslint/config-array@0.19.2':
    dependencies:
      '@eslint/object-schema': 2.1.6
      debug: 4.4.0
      minimatch: 3.1.2
    transitivePeerDependencies:
      - supports-color
  '@eslint/core@0.10.0':
    dependencies:
      '@types/json-schema': 7.0.15
  '@eslint/core@0.11.0':
    dependencies:
      '@types/json-schema': 7.0.15
  '@eslint/eslintrc@3.2.0':
    dependencies:
      ajv: 6.12.6
      debug: 4.4.0
      espree: 10.3.0
      globals: 14.0.0
      ignore: 5.3.2
      import-fresh: 3.3.1
      js-yaml: 4.1.0
      minimatch: 3.1.2
      strip-json-comments: 3.1.1
    transitivePeerDependencies:
      - supports-color
  '@eslint/js@9.20.0': {}
  '@eslint/object-schema@2.1.6': {}
  '@eslint/plugin-kit@0.2.5':
    dependencies:
      '@eslint/core': 0.10.0
      levn: 0.4.1
  '@humanfs/core@0.19.1': {}
  '@humanfs/node@0.16.6':
    dependencies:
      '@humanfs/core': 0.19.1
      '@humanwhocodes/retry': 0.3.1
  '@humanwhocodes/module-importer@1.0.1': {}
  '@humanwhocodes/retry@0.3.1': {}
  '@humanwhocodes/retry@0.4.1': {}
  '@isaacs/cliui@8.0.2':
    dependencies:
      string-width: 5.1.2
      string-width-cjs: string-width@4.2.3
      strip-ansi: 7.1.0
      strip-ansi-cjs: strip-ansi@6.0.1
      wrap-ansi: 8.1.0
      wrap-ansi-cjs: wrap-ansi@7.0.0
  '@jridgewell/sourcemap-codec@1.5.0': {}
  '@nodelib/fs.scandir@2.1.5':
    dependencies:
      '@nodelib/fs.stat': 2.0.5
      run-parallel: 1.2.0
  '@nodelib/fs.stat@2.0.5': {}
  '@nodelib/fs.walk@1.2.8':
    dependencies:
      '@nodelib/fs.scandir': 2.1.5
      fastq: 1.19.0
  '@pkgjs/parseargs@0.11.0':
    optional: true
  '@rollup/plugin-typescript@11.1.6(rollup@4.34.8)(tslib@2.8.1)(typescript@5.7.3)':
    dependencies:
      '@rollup/pluginutils': 5.1.4(rollup@4.34.8)
      resolve: 1.22.10
      typescript: 5.7.3
    optionalDependencies:
      rollup: 4.34.8
      tslib: 2.8.1
  '@rollup/pluginutils@5.1.4(rollup@4.34.8)':
    dependencies:
      '@types/estree': 1.0.6
      estree-walker: 2.0.2
      picomatch: 4.0.2
    optionalDependencies:
      rollup: 4.34.8
  '@rollup/rollup-android-arm-eabi@4.34.8':
    optional: true
  '@rollup/rollup-android-arm64@4.34.8':
    optional: true
  '@rollup/rollup-darwin-arm64@4.34.8':
    optional: true
  '@rollup/rollup-darwin-x64@4.34.8':
    optional: true
  '@rollup/rollup-freebsd-arm64@4.34.8':
    optional: true
  '@rollup/rollup-freebsd-x64@4.34.8':
    optional: true
  '@rollup/rollup-linux-arm-gnueabihf@4.34.8':
    optional: true
  '@rollup/rollup-linux-arm-musleabihf@4.34.8':
    optional: true
  '@rollup/rollup-linux-arm64-gnu@4.34.8':
    optional: true
  '@rollup/rollup-linux-arm64-musl@4.34.8':
    optional: true
  '@rollup/rollup-linux-loongarch64-gnu@4.34.8':
    optional: true
  '@rollup/rollup-linux-powerpc64le-gnu@4.34.8':
    optional: true
  '@rollup/rollup-linux-riscv64-gnu@4.34.8':
    optional: true
  '@rollup/rollup-linux-s390x-gnu@4.34.8':
    optional: true
  '@rollup/rollup-linux-x64-gnu@4.34.8':
    optional: true
  '@rollup/rollup-linux-x64-musl@4.34.8':
    optional: true
  '@rollup/rollup-win32-arm64-msvc@4.34.8':
    optional: true
  '@rollup/rollup-win32-ia32-msvc@4.34.8':
    optional: true
  '@rollup/rollup-win32-x64-msvc@4.34.8':
    optional: true
  '@trysound/sax@0.2.0': {}
  '@types/estree@1.0.6': {}
  '@types/json-schema@7.0.15': {}
  '@types/lodash@4.17.15': {}
  '@types/node@20.17.19':
    dependencies:
      undici-types: 6.19.8
  '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.20.1)(typescript@5.7.3))(eslint@9.20.1)(typescript@5.7.3)':
    dependencies:
      '@eslint-community/regexpp': 4.12.1
      '@typescript-eslint/parser': 7.18.0(eslint@9.20.1)(typescript@5.7.3)
      '@typescript-eslint/scope-manager': 7.18.0
      '@typescript-eslint/type-utils': 7.18.0(eslint@9.20.1)(typescript@5.7.3)
      '@typescript-eslint/utils': 7.18.0(eslint@9.20.1)(typescript@5.7.3)
      '@typescript-eslint/visitor-keys': 7.18.0
      eslint: 9.20.1
      graphemer: 1.4.0
      ignore: 5.3.2
      natural-compare: 1.4.0
      ts-api-utils: 1.4.3(typescript@5.7.3)
    optionalDependencies:
      typescript: 5.7.3
    transitivePeerDependencies:
      - supports-color
  '@typescript-eslint/parser@7.18.0(eslint@9.20.1)(typescript@5.7.3)':
    dependencies:
      '@typescript-eslint/scope-manager': 7.18.0
      '@typescript-eslint/types': 7.18.0
      '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.3)
      '@typescript-eslint/visitor-keys': 7.18.0
      debug: 4.4.0
      eslint: 9.20.1
    optionalDependencies:
      typescript: 5.7.3
    transitivePeerDependencies:
      - supports-color
  '@typescript-eslint/scope-manager@7.18.0':
    dependencies:
      '@typescript-eslint/types': 7.18.0
      '@typescript-eslint/visitor-keys': 7.18.0
  '@typescript-eslint/type-utils@7.18.0(eslint@9.20.1)(typescript@5.7.3)':
    dependencies:
      '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.3)
      '@typescript-eslint/utils': 7.18.0(eslint@9.20.1)(typescript@5.7.3)
      debug: 4.4.0
      eslint: 9.20.1
      ts-api-utils: 1.4.3(typescript@5.7.3)
    optionalDependencies:
      typescript: 5.7.3
    transitivePeerDependencies:
      - supports-color
  '@typescript-eslint/types@7.18.0': {}
  '@typescript-eslint/typescript-estree@7.18.0(typescript@5.7.3)':
    dependencies:
      '@typescript-eslint/types': 7.18.0
      '@typescript-eslint/visitor-keys': 7.18.0
      debug: 4.4.0
      globby: 11.1.0
      is-glob: 4.0.3
      minimatch: 9.0.5
      semver: 7.7.1
      ts-api-utils: 1.4.3(typescript@5.7.3)
    optionalDependencies:
      typescript: 5.7.3
    transitivePeerDependencies:
      - supports-color
  '@typescript-eslint/utils@7.18.0(eslint@9.20.1)(typescript@5.7.3)':
    dependencies:
      '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1)
      '@typescript-eslint/scope-manager': 7.18.0
      '@typescript-eslint/types': 7.18.0
      '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.3)
      eslint: 9.20.1
    transitivePeerDependencies:
      - supports-color
      - typescript
  '@typescript-eslint/visitor-keys@7.18.0':
    dependencies:
      '@typescript-eslint/types': 7.18.0
      eslint-visitor-keys: 3.4.3
  '@vue/compiler-core@3.5.13':
    dependencies:
      '@babel/parser': 7.26.9
      '@vue/shared': 3.5.13
      entities: 4.5.0
      estree-walker: 2.0.2
      source-map-js: 1.2.1
  '@vue/compiler-dom@3.5.13':
    dependencies:
      '@vue/compiler-core': 3.5.13
      '@vue/shared': 3.5.13
  '@vue/compiler-sfc@3.5.13':
    dependencies:
      '@babel/parser': 7.26.9
      '@vue/compiler-core': 3.5.13
      '@vue/compiler-dom': 3.5.13
      '@vue/compiler-ssr': 3.5.13
      '@vue/shared': 3.5.13
      estree-walker: 2.0.2
      magic-string: 0.30.17
      postcss: 8.5.2
      source-map-js: 1.2.1
  '@vue/compiler-ssr@3.5.13':
    dependencies:
      '@vue/compiler-dom': 3.5.13
      '@vue/shared': 3.5.13
  '@vue/shared@3.5.13': {}
  acorn-jsx@5.3.2(acorn@8.14.0):
    dependencies:
      acorn: 8.14.0
  acorn@8.14.0: {}
  ajv@6.12.6:
    dependencies:
      fast-deep-equal: 3.1.3
      fast-json-stable-stringify: 2.1.0
      json-schema-traverse: 0.4.1
      uri-js: 4.4.1
  ansi-regex@5.0.1: {}
  ansi-regex@6.1.0: {}
  ansi-styles@4.3.0:
    dependencies:
      color-convert: 2.0.1
  ansi-styles@6.2.1: {}
  argparse@2.0.1: {}
  array-union@2.1.0: {}
  asynckit@0.4.0: {}
  axios@1.7.9:
    dependencies:
      follow-redirects: 1.15.9
      form-data: 4.0.2
      proxy-from-env: 1.1.0
    transitivePeerDependencies:
      - debug
  balanced-match@1.0.2: {}
  boolbase@1.0.0: {}
  brace-expansion@1.1.11:
    dependencies:
      balanced-match: 1.0.2
      concat-map: 0.0.1
  brace-expansion@2.0.1:
    dependencies:
      balanced-match: 1.0.2
  braces@3.0.3:
    dependencies:
      fill-range: 7.1.1
  call-bind-apply-helpers@1.0.2:
    dependencies:
      es-errors: 1.3.0
      function-bind: 1.1.2
  callsites@3.1.0: {}
  chalk@4.1.2:
    dependencies:
      ansi-styles: 4.3.0
      supports-color: 7.2.0
  color-convert@2.0.1:
    dependencies:
      color-name: 1.1.4
  color-name@1.1.4: {}
  combined-stream@1.0.8:
    dependencies:
      delayed-stream: 1.0.0
  commander@7.2.0: {}
  concat-map@0.0.1: {}
  cross-spawn@7.0.6:
    dependencies:
      path-key: 3.1.1
      shebang-command: 2.0.0
      which: 2.0.2
  css-select@5.1.0:
    dependencies:
      boolbase: 1.0.0
      css-what: 6.1.0
      domhandler: 5.0.3
      domutils: 3.2.2
      nth-check: 2.1.1
  css-tree@2.2.1:
    dependencies:
      mdn-data: 2.0.28
      source-map-js: 1.2.1
  css-tree@2.3.1:
    dependencies:
      mdn-data: 2.0.30
      source-map-js: 1.2.1
  css-what@6.1.0: {}
  csso@5.0.5:
    dependencies:
      css-tree: 2.2.1
  debug@4.4.0:
    dependencies:
      ms: 2.1.3
  deep-is@0.1.4: {}
  delayed-stream@1.0.0: {}
  dir-glob@3.0.1:
    dependencies:
      path-type: 4.0.0
  dom-serializer@2.0.0:
    dependencies:
      domelementtype: 2.3.0
      domhandler: 5.0.3
      entities: 4.5.0
  domelementtype@2.3.0: {}
  domhandler@5.0.3:
    dependencies:
      domelementtype: 2.3.0
  domutils@3.2.2:
    dependencies:
      dom-serializer: 2.0.0
      domelementtype: 2.3.0
      domhandler: 5.0.3
  dunder-proto@1.0.1:
    dependencies:
      call-bind-apply-helpers: 1.0.2
      es-errors: 1.3.0
      gopd: 1.2.0
  eastasianwidth@0.2.0: {}
  emoji-regex@8.0.0: {}
  emoji-regex@9.2.2: {}
  entities@4.5.0: {}
  es-define-property@1.0.1: {}
  es-errors@1.3.0: {}
  es-object-atoms@1.1.1:
    dependencies:
      es-errors: 1.3.0
  es-set-tostringtag@2.1.0:
    dependencies:
      es-errors: 1.3.0
      get-intrinsic: 1.2.7
      has-tostringtag: 1.0.2
      hasown: 2.0.2
  esbuild@0.21.5:
    optionalDependencies:
      '@esbuild/aix-ppc64': 0.21.5
      '@esbuild/android-arm': 0.21.5
      '@esbuild/android-arm64': 0.21.5
      '@esbuild/android-x64': 0.21.5
      '@esbuild/darwin-arm64': 0.21.5
      '@esbuild/darwin-x64': 0.21.5
      '@esbuild/freebsd-arm64': 0.21.5
      '@esbuild/freebsd-x64': 0.21.5
      '@esbuild/linux-arm': 0.21.5
      '@esbuild/linux-arm64': 0.21.5
      '@esbuild/linux-ia32': 0.21.5
      '@esbuild/linux-loong64': 0.21.5
      '@esbuild/linux-mips64el': 0.21.5
      '@esbuild/linux-ppc64': 0.21.5
      '@esbuild/linux-riscv64': 0.21.5
      '@esbuild/linux-s390x': 0.21.5
      '@esbuild/linux-x64': 0.21.5
      '@esbuild/netbsd-x64': 0.21.5
      '@esbuild/openbsd-x64': 0.21.5
      '@esbuild/sunos-x64': 0.21.5
      '@esbuild/win32-arm64': 0.21.5
      '@esbuild/win32-ia32': 0.21.5
      '@esbuild/win32-x64': 0.21.5
  escape-string-regexp@4.0.0: {}
  eslint-scope@8.2.0:
    dependencies:
      esrecurse: 4.3.0
      estraverse: 5.3.0
  eslint-visitor-keys@3.4.3: {}
  eslint-visitor-keys@4.2.0: {}
  eslint@9.20.1:
    dependencies:
      '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1)
      '@eslint-community/regexpp': 4.12.1
      '@eslint/config-array': 0.19.2
      '@eslint/core': 0.11.0
      '@eslint/eslintrc': 3.2.0
      '@eslint/js': 9.20.0
      '@eslint/plugin-kit': 0.2.5
      '@humanfs/node': 0.16.6
      '@humanwhocodes/module-importer': 1.0.1
      '@humanwhocodes/retry': 0.4.1
      '@types/estree': 1.0.6
      '@types/json-schema': 7.0.15
      ajv: 6.12.6
      chalk: 4.1.2
      cross-spawn: 7.0.6
      debug: 4.4.0
      escape-string-regexp: 4.0.0
      eslint-scope: 8.2.0
      eslint-visitor-keys: 4.2.0
      espree: 10.3.0
      esquery: 1.6.0
      esutils: 2.0.3
      fast-deep-equal: 3.1.3
      file-entry-cache: 8.0.0
      find-up: 5.0.0
      glob-parent: 6.0.2
      ignore: 5.3.2
      imurmurhash: 0.1.4
      is-glob: 4.0.3
      json-stable-stringify-without-jsonify: 1.0.1
      lodash.merge: 4.6.2
      minimatch: 3.1.2
      natural-compare: 1.4.0
      optionator: 0.9.4
    transitivePeerDependencies:
      - supports-color
  espree@10.3.0:
    dependencies:
      acorn: 8.14.0
      acorn-jsx: 5.3.2(acorn@8.14.0)
      eslint-visitor-keys: 4.2.0
  esquery@1.6.0:
    dependencies:
      estraverse: 5.3.0
  esrecurse@4.3.0:
    dependencies:
      estraverse: 5.3.0
  estraverse@5.3.0: {}
  estree-walker@2.0.2: {}
  esutils@2.0.3: {}
  fast-deep-equal@3.1.3: {}
  fast-glob@3.3.3:
    dependencies:
      '@nodelib/fs.stat': 2.0.5
      '@nodelib/fs.walk': 1.2.8
      glob-parent: 5.1.2
      merge2: 1.4.1
      micromatch: 4.0.8
  fast-json-stable-stringify@2.1.0: {}
  fast-levenshtein@2.0.6: {}
  fastq@1.19.0:
    dependencies:
      reusify: 1.0.4
  file-entry-cache@8.0.0:
    dependencies:
      flat-cache: 4.0.1
  fill-range@7.1.1:
    dependencies:
      to-regex-range: 5.0.1
  find-up@5.0.0:
    dependencies:
      locate-path: 6.0.0
      path-exists: 4.0.0
  flat-cache@4.0.1:
    dependencies:
      flatted: 3.3.2
      keyv: 4.5.4
  flatted@3.3.2: {}
  follow-redirects@1.15.9: {}
  foreground-child@3.3.0:
    dependencies:
      cross-spawn: 7.0.6
      signal-exit: 4.1.0
  form-data@4.0.2:
    dependencies:
      asynckit: 0.4.0
      combined-stream: 1.0.8
      es-set-tostringtag: 2.1.0
      mime-types: 2.1.35
  fsevents@2.3.3:
    optional: true
  function-bind@1.1.2: {}
  get-intrinsic@1.2.7:
    dependencies:
      call-bind-apply-helpers: 1.0.2
      es-define-property: 1.0.1
      es-errors: 1.3.0
      es-object-atoms: 1.1.1
      function-bind: 1.1.2
      get-proto: 1.0.1
      gopd: 1.2.0
      has-symbols: 1.1.0
      hasown: 2.0.2
      math-intrinsics: 1.1.0
  get-proto@1.0.1:
    dependencies:
      dunder-proto: 1.0.1
      es-object-atoms: 1.1.1
  glob-parent@5.1.2:
    dependencies:
      is-glob: 4.0.3
  glob-parent@6.0.2:
    dependencies:
      is-glob: 4.0.3
  glob@10.4.5:
    dependencies:
      foreground-child: 3.3.0
      jackspeak: 3.4.3
      minimatch: 9.0.5
      minipass: 7.1.2
      package-json-from-dist: 1.0.1
      path-scurry: 1.11.1
  globals@14.0.0: {}
  globby@11.1.0:
    dependencies:
      array-union: 2.1.0
      dir-glob: 3.0.1
      fast-glob: 3.3.3
      ignore: 5.3.2
      merge2: 1.4.1
      slash: 3.0.0
  gopd@1.2.0: {}
  graphemer@1.4.0: {}
  has-flag@4.0.0: {}
  has-symbols@1.1.0: {}
  has-tostringtag@1.0.2:
    dependencies:
      has-symbols: 1.1.0
  hasown@2.0.2:
    dependencies:
      function-bind: 1.1.2
  ignore@5.3.2: {}
  import-fresh@3.3.1:
    dependencies:
      parent-module: 1.0.1
      resolve-from: 4.0.0
  imurmurhash@0.1.4: {}
  is-core-module@2.16.1:
    dependencies:
      hasown: 2.0.2
  is-extglob@2.1.1: {}
  is-fullwidth-code-point@3.0.0: {}
  is-glob@4.0.3:
    dependencies:
      is-extglob: 2.1.1
  is-number@7.0.0: {}
  isexe@2.0.0: {}
  jackspeak@3.4.3:
    dependencies:
      '@isaacs/cliui': 8.0.2
    optionalDependencies:
      '@pkgjs/parseargs': 0.11.0
  js-yaml@4.1.0:
    dependencies:
      argparse: 2.0.1
  json-buffer@3.0.1: {}
  json-schema-traverse@0.4.1: {}
  json-stable-stringify-without-jsonify@1.0.1: {}
  keyv@4.5.4:
    dependencies:
      json-buffer: 3.0.1
  levn@0.4.1:
    dependencies:
      prelude-ls: 1.2.1
      type-check: 0.4.0
  locate-path@6.0.0:
    dependencies:
      p-locate: 5.0.0
  lodash.merge@4.6.2: {}
  lodash@4.17.21: {}
  lru-cache@10.4.3: {}
  magic-string@0.30.17:
    dependencies:
      '@jridgewell/sourcemap-codec': 1.5.0
  math-intrinsics@1.1.0: {}
  mdn-data@2.0.28: {}
  mdn-data@2.0.30: {}
  merge2@1.4.1: {}
  micromatch@4.0.8:
    dependencies:
      braces: 3.0.3
      picomatch: 2.3.1
  mime-db@1.52.0: {}
  mime-types@2.1.35:
    dependencies:
      mime-db: 1.52.0
  minimatch@3.1.2:
    dependencies:
      brace-expansion: 1.1.11
  minimatch@9.0.5:
    dependencies:
      brace-expansion: 2.0.1
  minipass@7.1.2: {}
  ms@2.1.3: {}
  nanoid@3.3.8: {}
  natural-compare@1.4.0: {}
  nth-check@2.1.1:
    dependencies:
      boolbase: 1.0.0
  optionator@0.9.4:
    dependencies:
      deep-is: 0.1.4
      fast-levenshtein: 2.0.6
      levn: 0.4.1
      prelude-ls: 1.2.1
      type-check: 0.4.0
      word-wrap: 1.2.5
  p-limit@3.1.0:
    dependencies:
      yocto-queue: 0.1.0
  p-locate@5.0.0:
    dependencies:
      p-limit: 3.1.0
  package-json-from-dist@1.0.1: {}
  parent-module@1.0.1:
    dependencies:
      callsites: 3.1.0
  path-exists@4.0.0: {}
  path-key@3.1.1: {}
  path-parse@1.0.7: {}
  path-scurry@1.11.1:
    dependencies:
      lru-cache: 10.4.3
      minipass: 7.1.2
  path-type@4.0.0: {}
  picocolors@1.1.1: {}
  picomatch@2.3.1: {}
  picomatch@4.0.2: {}
  postcss@8.5.2:
    dependencies:
      nanoid: 3.3.8
      picocolors: 1.1.1
      source-map-js: 1.2.1
  prelude-ls@1.2.1: {}
  prettier@3.5.1: {}
  proxy-from-env@1.1.0: {}
  punycode@2.3.1: {}
  queue-microtask@1.2.3: {}
  resolve-from@4.0.0: {}
  resolve@1.22.10:
    dependencies:
      is-core-module: 2.16.1
      path-parse: 1.0.7
      supports-preserve-symlinks-flag: 1.0.0
  reusify@1.0.4: {}
  rollup@4.34.8:
    dependencies:
      '@types/estree': 1.0.6
    optionalDependencies:
      '@rollup/rollup-android-arm-eabi': 4.34.8
      '@rollup/rollup-android-arm64': 4.34.8
      '@rollup/rollup-darwin-arm64': 4.34.8
      '@rollup/rollup-darwin-x64': 4.34.8
      '@rollup/rollup-freebsd-arm64': 4.34.8
      '@rollup/rollup-freebsd-x64': 4.34.8
      '@rollup/rollup-linux-arm-gnueabihf': 4.34.8
      '@rollup/rollup-linux-arm-musleabihf': 4.34.8
      '@rollup/rollup-linux-arm64-gnu': 4.34.8
      '@rollup/rollup-linux-arm64-musl': 4.34.8
      '@rollup/rollup-linux-loongarch64-gnu': 4.34.8
      '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8
      '@rollup/rollup-linux-riscv64-gnu': 4.34.8
      '@rollup/rollup-linux-s390x-gnu': 4.34.8
      '@rollup/rollup-linux-x64-gnu': 4.34.8
      '@rollup/rollup-linux-x64-musl': 4.34.8
      '@rollup/rollup-win32-arm64-msvc': 4.34.8
      '@rollup/rollup-win32-ia32-msvc': 4.34.8
      '@rollup/rollup-win32-x64-msvc': 4.34.8
      fsevents: 2.3.3
  run-parallel@1.2.0:
    dependencies:
      queue-microtask: 1.2.3
  semver@7.7.1: {}
  shebang-command@2.0.0:
    dependencies:
      shebang-regex: 3.0.0
  shebang-regex@3.0.0: {}
  signal-exit@4.1.0: {}
  slash@3.0.0: {}
  source-map-js@1.2.1: {}
  string-width@4.2.3:
    dependencies:
      emoji-regex: 8.0.0
      is-fullwidth-code-point: 3.0.0
      strip-ansi: 6.0.1
  string-width@5.1.2:
    dependencies:
      eastasianwidth: 0.2.0
      emoji-regex: 9.2.2
      strip-ansi: 7.1.0
  strip-ansi@6.0.1:
    dependencies:
      ansi-regex: 5.0.1
  strip-ansi@7.1.0:
    dependencies:
      ansi-regex: 6.1.0
  strip-json-comments@3.1.1: {}
  supports-color@7.2.0:
    dependencies:
      has-flag: 4.0.0
  supports-preserve-symlinks-flag@1.0.0: {}
  svgo@3.3.2:
    dependencies:
      '@trysound/sax': 0.2.0
      commander: 7.2.0
      css-select: 5.1.0
      css-tree: 2.3.1
      css-what: 6.1.0
      csso: 5.0.5
      picocolors: 1.1.1
  to-regex-range@5.0.1:
    dependencies:
      is-number: 7.0.0
  ts-api-utils@1.4.3(typescript@5.7.3):
    dependencies:
      typescript: 5.7.3
  tslib@2.8.1: {}
  type-check@0.4.0:
    dependencies:
      prelude-ls: 1.2.1
  typescript@5.7.3: {}
  undici-types@6.19.8: {}
  uri-js@4.4.1:
    dependencies:
      punycode: 2.3.1
  vite@5.4.14(@types/node@20.17.19):
    dependencies:
      esbuild: 0.21.5
      postcss: 8.5.2
      rollup: 4.34.8
    optionalDependencies:
      '@types/node': 20.17.19
      fsevents: 2.3.3
  which@2.0.2:
    dependencies:
      isexe: 2.0.0
  word-wrap@1.2.5: {}
  wrap-ansi@7.0.0:
    dependencies:
      ansi-styles: 4.3.0
      string-width: 4.2.3
      strip-ansi: 6.0.1
  wrap-ansi@8.1.0:
    dependencies:
      ansi-styles: 6.2.1
      string-width: 5.1.2
      strip-ansi: 7.1.0
  yocto-queue@0.1.0: {}
packages/vite-plugin/src/config.ts
@@ -1,10 +1,9 @@
import type { Type } from "../types";
import type { Config } from "../types";
export const config = {
    type: "admin" as Type,
export const config: Config.Data = {
    type: "admin",
    reqUrl: "",
    demo: false,
    nameTag: true,
    eps: {
        enable: true,
        api: "",
@@ -12,7 +11,7 @@
        mapping: [
            {
                // 自定义匹配
                custom: ({ propertyName, type }: { propertyName: string; type: string }) => {
                custom: ({ propertyName, type }) => {
                    // 如果没有,返回null或者不返回,则继续遍历其他匹配规则
                    return null;
                },
@@ -37,25 +36,6 @@
                type: "BigInt",
                test: ["bigint"],
            },
            {
                type: "any",
                test: ["json"],
            },
        ],
    },
    svg: {
        skipNames: ["base"],
    },
    tailwind: {
        enable: true,
        remUnit: 14,
        remPrecision: 6,
        rpxRatio: 2,
        darkTextClass: "dark:text-surface-50",
    },
    uniapp: {
        isPlugin: false,
    },
    clean: false,
    utsPlatform: "web",
};
packages/vite-plugin/src/ctx/index.ts
@@ -13,7 +13,7 @@
        serviceLang: "Node",
    };
    if (config.type == "app" || config.type == "uniapp-x") {
    if (config.type == "app") {
        const manifest = readFile(rootDir("manifest.json"), true);
        // 文件路径
@@ -28,13 +28,6 @@
        // 删除临时页面
        ctx.pages = ctx.pages?.filter((e) => !e.isTemp);
        ctx.subPackages = ctx.subPackages?.filter((e) => !e.isTemp);
        // 删除不需要的数据
        for (const i in ctx) {
            if (!["pages", "subPackages", "tabBar", "globalStyle", "uniIdRouter"].includes(i)) {
                delete ctx[i];
            }
        }
        // 加载 uni_modules 配置文件
        const files = await glob(rootDir("uni_modules") + "/**/pages_init.json", {
packages/vite-plugin/src/eps/index.ts
@@ -1,24 +1,17 @@
import { createDir, error, firstUpperCase, readFile, rootDir, toCamel } from "../utils";
import { join } from "path";
import axios from "axios";
import { compact, isEmpty, last, uniqBy, values } from "lodash";
import { isEmpty, last, values } from "lodash";
import { createWriteStream } from "fs";
import prettier from "prettier";
import { config } from "../config";
import type { Eps } from "../../types";
import { flatten } from "../uniapp-x/flatten";
import { interfaceToType } from "../uniapp-x/utils";
// 全局 service 对象,用于存储服务结构
const service = {};
// eps 实体列表
let list: Eps.Entity[] = [];
/**
 * 获取 eps 请求地址
 * @returns {string} eps url
 */
function getEpsUrl(): string {
// 获取请求地址
function getEpsUrl() {
    let url = config.eps.api;
    if (!url) {
@@ -27,9 +20,9 @@
    switch (url) {
        case "app":
        case "uniapp-x":
            url = "/app/base/comm/eps";
            break;
        case "admin":
            url = "/admin/base/open/eps";
            break;
@@ -38,30 +31,126 @@
    return url;
}
/**
 * 获取 eps 路径
 * @param filename 文件名
 * @returns {string} 完整路径
 */
function getEpsPath(filename?: string): string {
// 获取路径
function getEpsPath(filename?: string) {
    return join(
        config.type == "admin" ? config.eps.dist : rootDir(config.eps.dist),
        filename || "",
    );
}
/**
 * 获取对象方法名(排除 namespace、permission 字段)
 * @param v 对象
 * @returns {string[]} 方法名数组
 */
function getNames(v: any): string[] {
// 获取方法名
function getNames(v: any) {
    return Object.keys(v).filter((e) => !["namespace", "permission"].includes(e));
}
/**
 * 获取字段类型
 */
// 找字段
function findColumns(sources: string[], item: Eps.Entity) {
    const columns = [item.columns, item.pageColumns].flat().filter(Boolean);
    return (sources || [])
        .map((e) => columns.find((c) => c.source == e))
        .filter(Boolean) as Eps.Column[];
}
// 格式化代码
async function formatCode(text: string) {
    return prettier.format(text, {
        parser: "typescript",
        useTabs: true,
        tabWidth: 4,
        endOfLine: "lf",
        semi: true,
        singleQuote: false,
        printWidth: 100,
        trailingComma: "none",
    });
}
// 获取数据
async function getData() {
    // 读取本地数据
    list = readFile(getEpsPath("eps.json"), true) || [];
    // 请求地址
    const url = config.reqUrl + getEpsUrl();
    // 请求数据
    await axios
        .get(url, {
            timeout: 5000,
        })
        .then((res) => {
            const { code, data, message } = res.data;
            if (code === 1000) {
                if (!isEmpty(data) && data) {
                    list = values(data).flat();
                }
            } else {
                error(`[cool-eps] ${message || "Failed to fetch data"}`);
            }
        })
        .catch(() => {
            error(`[cool-eps] API service is not running → ${url}`);
        });
    // 初始化处理
    list.forEach((e) => {
        if (!e.namespace) {
            e.namespace = "";
        }
        if (!e.api) {
            e.api = [];
        }
        if (!e.columns) {
            e.columns = [];
        }
        if (!e.search) {
            e.search = {
                fieldEq: findColumns(e.pageQueryOp?.fieldEq, e),
                fieldLike: findColumns(e.pageQueryOp?.fieldLike, e),
                keyWordLikeFields: findColumns(e.pageQueryOp?.keyWordLikeFields, e),
            };
        }
    });
}
// 创建 json 文件
function createJson() {
    const arr = list.map((e) => {
        return {
            prefix: e.prefix,
            name: e.name || "",
            api: e.api.map((e) => {
                return {
                    name: e.name,
                    method: e.method,
                    path: e.path,
                };
            }),
            search: e.search,
        };
    });
    const content = JSON.stringify(arr);
    const local_content = readFile(getEpsPath("eps.json"));
    // 是否需要更新
    const isUpdate = content != local_content;
    if (isUpdate) {
        createWriteStream(getEpsPath("eps.json"), {
            flags: "w",
        }).write(content);
    }
    return isUpdate;
}
// 创建描述文件
async function createDescribe({ list, service }: { list: Eps.Entity[]; service: any }) {
    // 获取类型
function getType({ propertyName, type }: any) {
    for (const map of config.eps.mapping) {
        if (map.custom) {
@@ -75,181 +164,35 @@
    return type;
}
/**
 * 格式化方法名,去除特殊字符
 */
    // 格式化方法名
function formatName(name: string) {
    return (name || "").replace(/[:,\s,\/,-]/g, "");
}
/**
 * 检查方法名是否合法(不包含特殊字符)
 */
function checkName(name: string) {
    return name && !["{", "}", ":"].some((e) => name.includes(e));
}
/**
 * 不支持 uniapp-x 平台显示
 */
function noUniappX(text: string, defaultText: string = "") {
    if (config.type == "uniapp-x") {
        return defaultText;
    } else {
        return text;
    }
}
/**
 * 查找字段
 * @param sources 字段 source 数组
 * @param item eps 实体
 * @returns {Eps.Column[]} 字段数组
 */
function findColumns(sources: string[], item: Eps.Entity): Eps.Column[] {
    const columns = [item.columns, item.pageColumns].flat().filter(Boolean);
    return (sources || [])
        .map((e) => columns.find((c) => c.source == e))
        .filter(Boolean) as Eps.Column[];
}
/**
 * 使用 prettier 格式化 TypeScript 代码
 * @param text 代码文本
 * @returns {Promise<string|null>} 格式化后的代码
 */
async function formatCode(text: string): Promise<string | null> {
    return prettier
        .format(text, {
            parser: "typescript",
            useTabs: true,
            tabWidth: 4,
            endOfLine: "lf",
            semi: true,
            singleQuote: false,
            printWidth: 100,
            trailingComma: "none",
        })
        .catch((err) => {
            console.log(err);
            error(`[cool-eps] File format error, please try again`);
            return null;
        });
}
/**
 * 获取 eps 数据(本地优先,远程兜底)
 */
async function getData() {
    // 读取本地 eps.json
    list = readFile(getEpsPath("eps.json"), true) || [];
    // 拼接请求地址
    const url = config.reqUrl + getEpsUrl();
    // 请求远程 eps 数据
    await axios
        .get(url, {
            timeout: 5000,
        })
        .then((res) => {
            const { code, data, message } = res.data;
            if (code === 1000) {
                if (!isEmpty(data) && data) {
                    list = values(data).flat();
                }
            } else {
                error(`[cool-eps] ${message || "Failed to fetch data"}`);
            }
        })
        .catch(() => {
            error(`[cool-eps] API service is not running → ${url}`);
        });
    // 初始化处理,补全缺省字段
    list.forEach((e) => {
        if (!e.namespace) e.namespace = "";
        if (!e.api) e.api = [];
        if (!e.columns) e.columns = [];
        if (!e.search) {
            e.search = {
                fieldEq: findColumns(e.pageQueryOp?.fieldEq, e),
                fieldLike: findColumns(e.pageQueryOp?.fieldLike, e),
                keyWordLikeFields: findColumns(e.pageQueryOp?.keyWordLikeFields, e),
            };
        }
    });
    if (config.type == "uniapp-x" || config.type == "app") {
        list = list.filter((e) => e.prefix.startsWith("/app") || e.prefix.startsWith("/admin"));
    }
}
/**
 * 创建 eps.json 文件
 * @returns {boolean} 是否有更新
 */
function createJson(): boolean {
    let data: any[] = [];
    if (config.type != "uniapp-x") {
        data = list.map((e) => {
            return {
                prefix: e.prefix,
                name: e.name || "",
                api: e.api.map((apiItem) => ({
                    name: apiItem.name,
                    method: apiItem.method,
                    path: apiItem.path,
                })),
                search: e.search,
            };
        });
    } else {
        data = list;
    }
    const content = JSON.stringify(data);
    const local_content = readFile(getEpsPath("eps.json"));
    // 判断是否需要更新
    const isUpdate = content != local_content;
    if (isUpdate) {
        createWriteStream(getEpsPath("eps.json"), {
            flags: "w",
        }).write(content);
    }
    return isUpdate;
}
/**
 * 创建 eps 类型描述文件(d.ts/ts)
 * @param param0 list: eps实体列表, service: service对象
 */
async function createDescribe({ list, service }: { list: Eps.Entity[]; service: any }) {
    /**
     * 创建 Entity 接口定义
     */
    // 创建 Entity
    function createEntity() {
        const ignore: string[] = [];
        let t0 = "";
        for (const item of list) {
            if (!checkName(item.name)) continue;
            if (formatName(item.name) == "BusinessInterface") {
                console.log(111);
            }
            if (!item.name) continue;
            let t = `interface ${formatName(item.name)} {`;
            // 合并 columns 和 pageColumns,去重
            const columns: Eps.Column[] = uniqBy(
                compact([...(item.columns || []), ...(item.pageColumns || [])]),
                "source",
            );
            // 合并多个列
            const columns: Eps.Column[] = [];
            [item.columns, item.pageColumns]
                .flat()
                .filter(Boolean)
                .forEach((e) => {
                    const d = columns.find((c) => c.source == e.source);
                    if (!d) {
                        columns.push(e);
                    }
                });
            for (const col of columns || []) {
                t += `
@@ -259,7 +202,7 @@
                    ${col.propertyName}?: ${getType({
                        propertyName: col.propertyName,
                        type: col.type,
                    })};
                    })}
                `;
            }
@@ -280,34 +223,23 @@
        return t0;
    }
    /**
     * 创建 Controller 接口定义
     */
    async function createController() {
    // 创建 Service
    async function createDts() {
        let controller = "";
        let chain = "";
        let pageResponse = "";
        /**
         * 递归处理 service 树,生成接口定义
         * @param d 当前节点
         * @param k 前缀
         */
        // 处理数据
        function deep(d: any, k?: string) {
            if (!k) k = "";
            for (const i in d) {
                const name = k + toCamel(firstUpperCase(formatName(i)));
                // 检查方法名
                if (!checkName(name)) continue;
                if (d[i].namespace) {
                    // 查找配置
                    const item = list.find((e) => (e.prefix || "") === `/${d[i].namespace}`);
                    if (item) {
                        //
                        let t = `interface ${name} {`;
                        // 插入方法
@@ -317,10 +249,9 @@
                            item.api.forEach((a) => {
                                // 方法名
                                const n = toCamel(formatName(a.name || last(a.path.split("/"))!));
                                // 检查方法名
                                if (!checkName(n)) return;
                                const n = toCamel(
                                    formatName(a.name || last(a.path.split("/")) || ""),
                                );
                                if (n) {
                                    // 参数类型
@@ -334,15 +265,14 @@
                                            q.push(`\n/** ${p.description}  */\n`);
                                        }
                                        // 检查参数名
                                        if (!checkName(p.name)) {
                                        if (p.name.includes(":")) {
                                            return false;
                                        }
                                        const a = `${p.name}${p.required ? "" : "?"}`;
                                        const b = `${p.schema.type || "string"}`;
                                        q.push(`${a}: ${b};`);
                                        q.push(`${a}: ${b},`);
                                    });
                                    if (isEmpty(q)) {
@@ -360,43 +290,35 @@
                                    switch (a.path) {
                                        case "/page":
                                            res = `${name}PageResponse`;
                                            pageResponse += `
                                                interface ${name}PageResponse {
                                                    pagination: PagePagination;
                                            res = `
                                            {
                                                pagination: { size: number; page: number; total: number; [key: string]: any };
                                                    list: ${en}[];
                                                [key: string]: any;
                                                }
                                            `;
                                            break;
                                        case "/list":
                                            res = `${en} []`;
                                            break;
                                        case "/info":
                                            res = en;
                                            break;
                                        default:
                                            res = "any";
                                            break;
                                    }
                                    // 方法描述
                                    if (config.type == "uniapp-x") {
                                        t += `
                                            /**
                                             * ${a.summary || n}
                                             */
                                            ${n}(data${q.length == 1 ? "?" : ""}: ${q.join("")}): Promise<any>;
                                        `;
                                    } else {
                                    // 描述
                                        t += `
                                            /**
                                             * ${a.summary || n}
                                             */
                                            ${n}(data${q.length == 1 ? "?" : ""}: ${q.join("")}): Promise<${res}>;
                                        `;
                                    }
                                    if (!permission.includes(n)) {
                                        permission.push(n);
@@ -405,25 +327,24 @@
                            });
                            // 权限标识
                            t += noUniappX(`
                            t += `
                                /**
                                 * 权限标识
                                 */
                                permission: { ${permission.map((e) => `${e}: string;`).join("\n")} };
                            `);
                            `;
                            // 权限状态
                            t += noUniappX(`
                            t += `
                                /**
                                 * 权限状态
                                 */
                                _permission: { ${permission.map((e) => `${e}: boolean;`).join("\n")} };
                            `);
                            `;
                            // 请求
                            t += noUniappX(`
                                request: Request;
                            `);
                            t += `
                                request: Service['request']
                            `;
                        }
                        t += "}\n\n";
@@ -434,99 +355,67 @@
                } else {
                    chain += `${formatName(i)}: {`;
                    deep(d[i], name);
                    chain += "};";
                    chain += "},";
                }
            }
        }
        // 遍历 service 树
        // 遍历
        deep(service);
        return `
            type json = any;
            ${await createDict()}
            interface PagePagination {
                size: number;
                page: number;
                total: number;
                [key: string]: any;
            };
            interface PageResponse<T> {
                pagination: PagePagination;
                list: T[];
                [key: string]: any;
            };
            ${pageResponse}
            ${controller}
            ${noUniappX(`interface RequestOptions {
            type Service = {
                /**
                 * 基础请求
                 */
                request(options?: {
                url: string;
                method?: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT';
                    method?: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
                data?: any;
                params?: any;
                headers?: any;
                timeout?: number;
                    headers?: {
                        authorization?: string;
                [key: string]: any;
            }`)}
            ${noUniappX("type Request = (options: RequestOptions) => Promise<any>;")}
            type Service = {
                ${noUniappX("request: Request;")}
                    },
                    timeout?: number;
                    proxy?: boolean;
                    [key: string]: any;
                }): Promise<any>;
                ${chain}
            }
            ${await createDict()}
        `;
    }
    // 组装文件内容
    let text = `
        ${createEntity()}
        ${await createController()}
    `;
    // 文件名
    let name = "eps.d.ts";
    if (config.type == "uniapp-x") {
        name = "eps.ts";
        text = text
            .replaceAll("interface ", "export interface ")
            .replaceAll("type ", "export type ")
            .replaceAll("[key: string]: any;", "");
        text = flatten(text);
        text = interfaceToType(text);
    } else {
        text = `
    // 文件内容
    const text = `
            declare namespace Eps {
                ${text}
            ${createEntity()}
            ${await createDts()}
            }
        `;
    }
    // 格式化文本内容
    // 文本内容
    const content = await formatCode(text);
    const local_content = readFile(getEpsPath(name));
    const local_content = readFile(getEpsPath("eps.d.ts"));
    // 是否需要更新
    if (content && content != local_content && list.length > 0) {
    if (content != local_content) {
        // 创建 eps 描述文件
        createWriteStream(getEpsPath(name), {
        createWriteStream(getEpsPath("eps.d.ts"), {
            flags: "w",
        }).write(content);
    }
}
/**
 * 构建 service 对象树
 */
// 创建 service
function createService() {
    // 路径第一层作为 id 标识
    const id = getEpsUrl().split("/")[1];
@@ -535,14 +424,10 @@
        // 请求地址
        const path = e.prefix[0] == "/" ? e.prefix.substring(1, e.prefix.length) : e.prefix;
        // 分隔路径,去除 id,转驼峰
        // 分隔路径
        const arr = path.replace(id, "").split("/").filter(Boolean).map(toCamel);
        /**
         * 递归构建 service 树
         * @param d 当前节点
         * @param i 当前索引
         */
        // 遍历
        function deep(d: any, i: number) {
            const k = arr[i];
@@ -552,6 +437,7 @@
                    if (!d[k]) {
                        d[k] = {};
                    }
                    deep(d[k], i + 1);
                } else {
                    // 不存在则创建
@@ -580,6 +466,7 @@
                    e.api.forEach((a) => {
                        // 方法名
                        const n = a.path.replace("/", "");
                        if (n && !/[-:]/g.test(n)) {
                            d[k][n] = a;
                        }
@@ -592,190 +479,50 @@
    });
}
/**
 * 创建 service 代码
 * @returns {string} service 代码
 */
function createServiceCode(): { content: string; types: string[] } {
    const types: string[] = [];
// 创建 dict
async function createDict() {
    const url = config.reqUrl + "/" + config.type + "/dict/info/types";
    let chain = "";
    /**
     * 递归处理 service 树,生成接口代码
     * @param d 当前节点
     * @param k 前缀
     */
    function deep(d: any, k?: string) {
        if (!k) k = "";
        for (const i in d) {
            if (["swagger"].includes(i)) {
                continue;
            }
            const name = k + toCamel(firstUpperCase(formatName(i)));
            // 检查方法名
            if (!checkName(name)) continue;
            if (d[i].namespace) {
                // 查找配置
                const item = list.find((e) => (e.prefix || "") === `/${d[i].namespace}`);
                if (item) {
                    //
                    let t = `{`;
                    // 插入方法
                    if (item.api) {
                        item.api.forEach((a) => {
                            // 方法名
                            const n = toCamel(formatName(a.name || last(a.path.split("/"))!));
                            // 检查方法名
                            if (!checkName(n)) return;
                            if (n) {
                                // 参数类型
                                let q: string[] = [];
                                // 参数列表
                                const { parameters = [] } = a.dts || {};
                                parameters.forEach((p) => {
                                    if (p.description) {
                                        q.push(`\n/** ${p.description}  */\n`);
                                    }
                                    // 检查参数名
                                    if (!checkName(p.name)) {
                                        return false;
                                    }
                                    const a = `${p.name}${p.required ? "" : "?"}`;
                                    const b = `${p.schema.type || "string"}`;
                                    q.push(`${a}: ${b}, `);
                                });
                                if (isEmpty(q)) {
                                    q = ["any"];
                                } else {
                                    q.unshift("{");
                                    q.push("}");
                                }
                                if (item.name) {
                                    types.push(item.name);
                                }
                                // 方法描述
                                t += `
                                    /**
                                     * ${a.summary || n}
                                     */
                                    ${n}(data?: any): Promise<any> {
                                        return request({
                                            url: "/${d[i].namespace}${a.path}",
                                            method: "${(a.method || "get").toLocaleUpperCase()}",
                                            data,
                                        });
                                    },
                                `;
                            }
                        });
                    }
                    t += `} as ${name}\n`;
                    types.push(name);
                    chain += `${formatName(i)}: ${t},\n`;
                }
            } else {
                chain += `${formatName(i)}: {`;
                deep(d[i], name);
                chain += `} as ${firstUpperCase(i)}Interface,`;
                types.push(`${firstUpperCase(i)}Interface`);
            }
        }
    }
    // 遍历 service 树
    deep(service);
    return {
        content: `{ ${chain} }`,
        types,
    };
}
/**
 * 获取字典类型定义
 * @returns {Promise<string>} 字典类型 type 定义
 */
async function createDict(): Promise<string> {
    let p = "";
    switch (config.type) {
        case "app":
        case "uniapp-x":
            p = "/app";
            break;
        case "admin":
            p = "/admin";
            break;
    }
    const url = config.reqUrl + p + "/dict/info/types";
    const text = await axios
    return axios
        .get(url)
        .then((res) => {
            const { code, data } = res.data as { code: number; data: any[] };
            if (code === 1000) {
                let v = "string";
                if (!isEmpty(data)) {
                    v = data.map((e) => `"${e.key}"`).join(" | ");
                }
                return `type DictKey = ${v}`;
            }
        })
        .catch(() => {
            error(`[cool-eps] Error:${url}`);
        });
    return text || "";
}
/**
 * 主入口:创建 eps 相关文件和 service
 */
// 创建 eps
export async function createEps() {
    if (config.eps.enable) {
        // 获取 eps 数据
        // 获取数据
        await getData();
        // 构建 service 对象
        // 创建 service
        createService();
        const serviceCode = createServiceCode();
        // 创建 eps 目录
        // 创建目录
        createDir(getEpsPath(), true);
        // 创建 eps.json 文件
        // 创建 json 文件
        const isUpdate = createJson();
        // 创建类型描述文件
        // 创建描述文件
        createDescribe({ service, list });
        return {
            service,
            serviceCode,
            list,
            isUpdate,
        };
packages/vite-plugin/src/index.ts
@@ -4,8 +4,7 @@
import { getProxyTarget } from "./proxy";
import type { Config } from "../types";
import { virtual } from "./virtual";
import { assign, merge } from "lodash";
import { uniappX } from "./uniapp-x";
import { merge } from "lodash";
export function cool(options: Config.Options) {
    // 应用类型,admin | app
@@ -14,26 +13,8 @@
    // 请求地址
    config.reqUrl = getProxyTarget(options.proxy);
    if (config.type == "uniapp-x") {
        // 编译平台
        config.utsPlatform = process.env.UNI_UTS_PLATFORM ?? "web";
        // 是否纯净版
        config.clean = options.clean ?? true;
        if (config.clean) {
            // 默认设置为测试地址
            config.reqUrl = "https://show.cool-admin.com/api";
        }
    }
    // 是否开启名称标签
    config.nameTag = options.nameTag ?? true;
    // svg
    if (options.svg) {
        assign(config.svg, options.svg);
    }
    // Eps
    if (options.eps) {
@@ -58,20 +39,5 @@
        }
    }
    // 如果类型为 uniapp-x,则关闭 eps
    if (config.type == "uniapp-x") {
        config.eps.enable = false;
    }
    // uniapp
    if (options.uniapp) {
        assign(config.uniapp, options.uniapp);
    }
    // tailwind
    if (options.tailwind) {
        assign(config.tailwind, options.tailwind);
    }
    return [base(), virtual(), uniappX(), demo(options.demo)];
    return [base(), virtual(), demo(options.demo)];
}
packages/vite-plugin/src/svg/index.ts
@@ -2,7 +2,6 @@
import { basename, extname } from "path";
import { rootDir } from "../utils";
import svgo from "svgo";
import { config } from "../config";
let svgIcons: string[] = [];
@@ -11,28 +10,12 @@
    const dirs = readdirSync(dir, {
        withFileTypes: true,
    });
    // 获取当前目录的模块名
    const moduleName = dir.match(/[/\\](?:src[/\\](?:plugins|modules)[/\\])([^/\\]+)/)?.[1] || "";
    for (const d of dirs) {
        if (d.isDirectory()) {
            arr.push(...findSvg(dir + d.name + "/"));
        } else {
            if (extname(d.name) == ".svg") {
                const baseName = basename(d.name, ".svg");
                // 判断是否需要跳过拼接模块名
                let shouldSkip = config.svg.skipNames?.includes(moduleName);
                // 跳过包含icon-
                if (baseName.includes("icon-")) {
                    shouldSkip = true;
                }
                const iconName = shouldSkip ? baseName : `${moduleName}-${baseName}`;
                svgIcons.push(iconName);
                svgIcons.push(basename(d.name, ".svg"));
                const svg = readFileSync(dir + d.name)
                    .toString()
@@ -54,7 +37,7 @@
                        if (!/(viewBox="[^>+].*?")/g.test($2)) {
                            content += `viewBox="0 0 ${width} ${height}"`;
                        }
                        return `<symbol id="icon-${iconName}" ${content}>`;
                        return `<symbol id="icon-${d.name.replace(".svg", "")}" ${content}>`;
                    })
                    .replace("</svg>", "</symbol>");
@@ -62,7 +45,6 @@
            }
        }
    }
    return arr;
}
packages/vite-plugin/src/utils/index.ts
@@ -7,7 +7,6 @@
export function rootDir(path: string) {
    switch (config.type) {
        case "app":
        case "uniapp-x":
            return join(process.env.UNI_INPUT_DIR!, path);
        default:
@@ -40,68 +39,12 @@
export function readFile(path: string, json?: boolean) {
    try {
        const content = fs.readFileSync(path, "utf8");
        return json ? JSON.parse(removeJsonComments(content)) : content;
        return json
            ? JSON.parse(content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, ""))
            : content;
    } catch (err) {}
    return "";
}
// 安全地移除JSON中的注释
function removeJsonComments(content: string): string {
    let result = "";
    let inString = false;
    let stringChar = "";
    let escaped = false;
    let i = 0;
    while (i < content.length) {
        const char = content[i];
        const nextChar = content[i + 1];
        // 处理字符串状态
        if (!inString && (char === '"' || char === "'")) {
            inString = true;
            stringChar = char;
            result += char;
        } else if (inString && char === stringChar && !escaped) {
            inString = false;
            stringChar = "";
            result += char;
        } else if (inString) {
            // 在字符串内,直接添加字符
            result += char;
            escaped = char === "\\" && !escaped;
        } else {
            // 不在字符串内,检查注释
            if (char === "/" && nextChar === "/") {
                // 单行注释,跳过到行尾
                while (i < content.length && content[i] !== "\n") {
                    i++;
                }
                if (i < content.length) {
                    result += content[i]; // 保留换行符
                }
            } else if (char === "/" && nextChar === "*") {
                // 多行注释,跳过到 */
                i += 2;
                while (i < content.length - 1) {
                    if (content[i] === "*" && content[i + 1] === "/") {
                        i += 2;
                        break;
                    }
                    i++;
                }
                continue;
            } else {
                result += char;
                escaped = false;
            }
        }
        i++;
    }
    return result;
}
// 写入文件
@@ -148,27 +91,4 @@
export function success(message: string) {
    console.log("\x1B[32m%s\x1B[0m", message);
}
/**
 * 比较两个版本号
 * @param version1 版本号1 (如: "1.2.3")
 * @param version2 版本号2 (如: "1.2.4")
 * @returns 1: version1 > version2, 0: 相等, -1: version1 < version2
 */
export function compareVersion(version1: string, version2: string): number {
    const v1Parts = version1.split(".").map(Number);
    const v2Parts = version2.split(".").map(Number);
    const maxLength = Math.max(v1Parts.length, v2Parts.length);
    for (let i = 0; i < maxLength; i++) {
        const v1Part = v1Parts[i] || 0;
        const v2Part = v2Parts[i] || 0;
        if (v1Part > v2Part) return 1;
        if (v1Part < v2Part) return -1;
    }
    return 0;
}
packages/vite-plugin/types/index.d.ts
@@ -1,4 +1,4 @@
export declare type Type = "admin" | "app" | "uniapp-x";
export declare type Type = "app" | "admin";
export declare namespace Eps {
    interface Column {
@@ -76,14 +76,11 @@
}
export declare namespace Config {
    type Type = "app" | "admin";
    interface Eps {
        // 是否开启Eps
        enable: boolean;
        // 请求地址
        api: "app" | "admin" | (string & {});
        // 输出目录
        dist: string;
        // 映射
        mapping: {
            type?: string;
            test?: string[];
@@ -91,39 +88,17 @@
        }[];
    }
    interface Options {
        // 应用类型
        type: Type;
        // 代理配置
        proxy?: any;
        // Eps
        type: Config.Type;
        proxy: any;
        eps?: Partial<Config.Eps>;
        // 是否开启演示模式
        demo?: boolean;
        // 是否开启名称标签
        nameTag?: boolean;
        // svg
        svg?: {
            // 跳过拼接模块名
            skipNames?: string[];
        };
        // tailwind
        tailwind?: {
            // 是否开启tailwind
            enable?: boolean;
            // 根元素字体大小
            remUnit?: number;
            // 小数位数
            remPrecision?: number;
            // 转换比例
            rpxRatio?: number;
            // 暗黑模式文本类名
            darkTextClass?: string;
        };
        // uniapp X
        uniapp?: {
            isPlugin?: boolean;
        };
        // 是否纯净版
        clean?: boolean;
    }
    interface Data {
        type: Config.Type;
        reqUrl: string;
        eps: Config.Eps;
        demo: boolean;
        [key: string]: any;
    }
}
pnpm-lock.yaml
Diff too large
src/config/index.ts
@@ -55,6 +55,11 @@
        token: []
    },
    // 高德地图配置
    amap: {
        key: ''
    },
    // 当前环境
    ...(isDev ? dev : prod)
};
src/modules/app/views/complain.vue
New file
@@ -0,0 +1,148 @@
<template>
    <cl-crud ref="Crud">
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <cl-filter label="状态">
                <cl-select :options="options.status" prop="status" :width="120" />
            </cl-filter>
            <cl-filter label="投诉类型">
                <cl-select :options="options.type" prop="type" :width="120" />
            </cl-filter>
            <cl-flex1 />
            <!-- 关键字搜索 -->
            <cl-search-key placeholder="搜索联系方式、用户、处理人" />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
        <!-- 新增、编辑 -->
        <cl-upsert ref="Upsert" />
    </cl-crud>
</template>
<script lang="ts" name="app-complain" setup>
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import { reactive, computed } from "vue";
import { useDict } from "/$/dict";
const { service } = useCool();
const { dict } = useDict();
const options = reactive({
    status: [
        {
            label: "未处理",
            value: 0,
            type: "warning"
        },
        {
            label: "已处理",
            value: 1
        }
    ],
    type: computed(() => {
        return dict.get("complainType");
    })
});
// cl-upsert
const Upsert = useUpsert({
    props: {
        labelWidth: 80
    },
    items: [
        {
            prop: "status",
            label: "状态",
            component: { name: "el-radio-group", options: options.status },
            required: true
        },
        {
            prop: "remark",
            label: "处理结果",
            component: {
                name: "el-input",
                props: {
                    type: "textarea",
                    rows: 4
                }
            }
        }
    ]
});
// cl-table
const Table = useTable({
    columns: [
        { type: "index", label: "#", width: 60 },
        { prop: "avatarUrl", label: "头像", component: { name: "cl-avatar" }, minWidth: 80 },
        { prop: "nickName", label: "用户", minWidth: 120 },
        { prop: "contact", label: "联系方式", minWidth: 120 },
        {
            prop: "content",
            label: "内容",
            component: { name: "cl-editor-preview", props: { name: "wang" } },
            minWidth: 120
        },
        {
            prop: "images",
            label: "图片",
            component: { name: "cl-image", props: { size: 60 } },
            minWidth: 100
        },
        {
            prop: "type",
            label: "类型",
            dict: options.type,
            dictFormatter(arr) {
                return arr.map((e) => e.label).join("、");
            },
            minWidth: 120
        },
        { prop: "status", label: "状态", dict: options.status, minWidth: 100 },
        { prop: "handlerName", label: "处理人", minWidth: 120 },
        { prop: "remark", label: "处理结果", showOverflowTooltip: true, minWidth: 200 },
        { prop: "createTime", label: "创建时间", sortable: "desc", minWidth: 160 },
        { prop: "updateTime", label: "更新时间", sortable: "custom", minWidth: 160 },
        {
            type: "op",
            width: 120,
            buttons({ scope }) {
                if (scope.row.status == 0) {
                    return ["edit"];
                }
                return [];
            }
        }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        dict: {
            label: {
                update: "处理"
            }
        },
        service: service.app.complain
    },
    (app) => {
        app.refresh();
    }
);
</script>
src/modules/app/views/feedback.vue
New file
@@ -0,0 +1,148 @@
<template>
    <cl-crud ref="Crud">
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <cl-filter label="状态">
                <cl-select :options="options.status" prop="status" :width="120" />
            </cl-filter>
            <cl-filter label="反馈类型">
                <cl-select :options="options.type" prop="type" :width="120" />
            </cl-filter>
            <cl-flex1 />
            <!-- 关键字搜索 -->
            <cl-search-key placeholder="搜索联系方式、用户、处理人" />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
        <!-- 新增、编辑 -->
        <cl-upsert ref="Upsert" />
    </cl-crud>
</template>
<script lang="ts" name="app-feedback" setup>
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import { computed, reactive } from "vue";
import { useDict } from "/$/dict";
const { service } = useCool();
const { dict } = useDict();
const options = reactive({
    status: [
        {
            label: "未处理",
            value: 0,
            type: "warning"
        },
        {
            label: "已处理",
            value: 1
        }
    ],
    type: computed(() => {
        return dict.get("feedbackType");
    })
});
// cl-upsert
const Upsert = useUpsert({
    props: {
        labelWidth: 80
    },
    items: [
        {
            prop: "status",
            label: "状态",
            component: { name: "el-radio-group", options: options.status },
            required: true
        },
        {
            prop: "remark",
            label: "处理结果",
            component: {
                name: "el-input",
                props: {
                    type: "textarea",
                    rows: 4
                }
            }
        }
    ]
});
// cl-table
const Table = useTable({
    columns: [
        { type: "index", label: "#", width: 60 },
        { prop: "avatarUrl", label: "头像", component: { name: "cl-avatar" }, minWidth: 80 },
        { prop: "nickName", label: "用户", minWidth: 120 },
        { prop: "contact", label: "联系方式", minWidth: 120 },
        {
            prop: "content",
            label: "内容",
            component: { name: "cl-editor-preview", props: { name: "wang" } },
            minWidth: 120
        },
        {
            prop: "images",
            label: "图片",
            component: { name: "cl-image", props: { size: 60 } },
            minWidth: 100
        },
        {
            prop: "type",
            label: "类型",
            dict: options.type,
            dictFormatter(arr) {
                return arr.map((e) => e.label).join("、");
            },
            minWidth: 120
        },
        { prop: "status", label: "状态", dict: options.status, minWidth: 100 },
        { prop: "handlerName", label: "处理人", minWidth: 120 },
        { prop: "remark", label: "处理结果", showOverflowTooltip: true, minWidth: 200 },
        { prop: "createTime", label: "创建时间", sortable: "desc", minWidth: 160 },
        { prop: "updateTime", label: "更新时间", sortable: "custom", minWidth: 160 },
        {
            type: "op",
            width: 120,
            buttons({ scope }) {
                if (scope.row.status == 0) {
                    return ["edit"];
                }
                return [];
            }
        }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        service: service.app.feedback,
        dict: {
            label: {
                update: "处理"
            }
        }
    },
    (app) => {
        app.refresh();
    }
);
</script>
src/modules/app/views/goods.vue
New file
@@ -0,0 +1,188 @@
<template>
    <cl-crud ref="Crud">
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <!-- 新增按钮 -->
            <cl-add-btn />
            <!-- 删除按钮 -->
            <cl-multi-delete-btn />
            <cl-filter label="状态">
                <cl-select :options="options.status" prop="status" :width="120" />
            </cl-filter>
            <cl-filter label="类型">
                <cl-select :options="options.type" prop="type" :width="120" />
            </cl-filter>
            <cl-flex1 />
            <!-- 关键字搜索 -->
            <cl-search-key placeholder="搜索标题" />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table">
                <template #column-tagColor="{ scope }">
                    <!-- 显示颜色 -->
                    <div
                        :style="{
                            backgroundColor: scope.row.tagColor,
                            height: '10px',
                            width: '10px',
                            borderRadius: '100%',
                            margin: 'auto'
                        }"
                    />
                </template>
            </cl-table>
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
        <!-- 新增、编辑 -->
        <cl-upsert ref="Upsert" />
    </cl-crud>
</template>
<script lang="ts" name="app-goods" setup>
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import { reactive } from "vue";
const { service } = useCool();
const options = reactive({
    status: [
        {
            label: "启用",
            value: 1
        },
        {
            label: "禁用",
            value: 0
        }
    ],
    type: [
        { label: "天", value: 0 },
        { label: "月", value: 1, type: "success" },
        { label: "年", value: 2, type: "warning" }
    ]
});
// cl-upsert
const Upsert = useUpsert({
    items: [
        { prop: "title", label: "标题", required: true, component: { name: "el-input" } },
        {
            prop: "description",
            label: "描述",
            component: {
                name: "el-input",
                props: {
                    type: "textarea",
                    rows: 3
                }
            }
        },
        {
            span: 12,
            required: true,
            prop: "price",
            label: "价格",
            hook: { bind: ["number"] },
            component: { name: "el-input-number", props: { min: 0 } }
        },
        {
            required: true,
            span: 12,
            prop: "originalPrice",
            label: "原价",
            hook: { bind: ["number"] },
            component: { name: "el-input-number", props: { min: 0 } }
        },
        {
            span: 12,
            prop: "type",
            label: "类型",
            component: {
                name: "el-radio-group",
                options: options.type
            },
            value: 0,
            required: true
        },
        {
            span: 12,
            prop: "duration",
            label: "时长",
            required: true,
            component: { name: "el-input-number", props: { min: 0 } }
        },
        { span: 12, prop: "tag", label: "标签", component: { name: "el-input" } },
        {
            span: 12,
            prop: "tagColor",
            label: "标签颜色",
            value: "#26A7FD",
            component: { name: "el-color-picker" }
        },
        {
            value: 0,
            prop: "sort",
            label: "排序",
            hook: { bind: ["number"] },
            component: { name: "el-input-number", props: { min: 0 } },
            required: true
        },
        {
            prop: "status",
            flex: false,
            value: 1,
            label: "状态",
            component: { name: "cl-switch" },
            required: true
        }
    ]
});
// cl-table
const Table = useTable({
    columns: [
        { type: "selection" },
        { prop: "title", label: "标题", minWidth: 150 },
        { prop: "price", label: "价格", minWidth: 120 },
        { prop: "originalPrice", label: "原价", minWidth: 120 },
        { prop: "description", label: "描述", showOverflowTooltip: true, minWidth: 200 },
        { prop: "status", label: "状态", component: { name: "cl-switch" }, minWidth: 80 },
        { prop: "sort", label: "排序", minWidth: 80 },
        {
            prop: "type",
            label: "类型",
            dict: options.type,
            minWidth: 80
        },
        { prop: "duration", label: "时长", minWidth: 80 },
        { prop: "tag", label: "标签", minWidth: 150 },
        { prop: "tagColor", label: "标签颜色", minWidth: 100 },
        { prop: "createTime", label: "创建时间", sortable: "desc", minWidth: 160 },
        { prop: "updateTime", label: "更新时间", sortable: "custom", minWidth: 160 },
        { type: "op", buttons: ["edit", "delete"] }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        service: service.app.goods
    },
    (app) => {
        app.refresh();
    }
);
</script>
src/modules/app/views/version.vue
New file
@@ -0,0 +1,177 @@
<template>
    <cl-crud ref="Crud">
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <!-- 新增按钮 -->
            <cl-add-btn />
            <!-- 删除按钮 -->
            <cl-multi-delete-btn />
            <cl-filter label="状态">
                <cl-select :options="options.status" prop="status" :width="120" />
            </cl-filter>
            <cl-filter label="类型">
                <cl-select :options="options.type" prop="type" :width="120" />
            </cl-filter>
            <cl-flex1 />
            <!-- 关键字搜索 -->
            <cl-search-key placeholder="搜索名称、版本" />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
        <!-- 新增、编辑 -->
        <cl-upsert ref="Upsert" />
    </cl-crud>
</template>
<script lang="ts" name="app-version" setup>
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import { useDict } from "/$/dict";
import { computed, reactive } from "vue";
const { service } = useCool();
const { dict } = useDict();
const options = reactive({
    // 状态
    status: [
        {
            label: "启用",
            value: 1,
            type: "success"
        },
        {
            label: "禁用",
            value: 0,
            type: "danger"
        }
    ],
    // 类型
    type: computed(() => {
        return dict.get("upgradeType");
    })
});
// cl-upsert
const Upsert = useUpsert({
    items: [
        { span: 12, prop: "name", label: "名称", required: true, component: { name: "el-input" } },
        {
            span: 12,
            prop: "version",
            label: "版本号",
            required: true,
            component: { name: "el-input" }
        },
        {
            prop: "description",
            label: "描述",
            required: true,
            component: {
                name: "el-input",
                props: {
                    type: "textarea",
                    rows: 4
                }
            }
        },
        {
            prop: "type",
            label: "类型",
            value: 0,
            component: { name: "el-radio-group", options: options.type },
            required: true
        },
        {
            prop: "url",
            label: "下载地址",
            component: { name: "cl-upload", props: { type: "file", limit: 1 } },
            required: true
        },
        {
            prop: "hotUpdate",
            label: "热更新",
            flex: false,
            value: 0,
            component: {
                name: "cl-switch"
            },
            required: true
        },
        {
            prop: "forceUpdate",
            label: "强制更新",
            flex: false,
            value: 0,
            component: {
                name: "cl-switch"
            },
            required: true
        },
        {
            prop: "status",
            label: "状态",
            value: 1,
            flex: false,
            component: { name: "cl-switch" },
            required: true
        }
    ]
});
// cl-table
const Table = useTable({
    columns: [
        { type: "selection" },
        { prop: "name", label: "名称", minWidth: 150 },
        { prop: "version", label: "版本号", minWidth: 100 },
        { prop: "type", label: "类型", dict: options.type, minWidth: 100 },
        { prop: "url", label: "下载地址", component: { name: "cl-link" }, minWidth: 120 },
        {
            prop: "forceUpdate",
            label: "强制更新",
            formatter(row) {
                return row.forceUpdate ? "是" : "否";
            },
            minWidth: 100
        },
        {
            prop: "hotUpdate",
            label: "热更新",
            formatter(row) {
                return row.hotUpdate ? "是" : "否";
            },
            minWidth: 100
        },
        { prop: "status", label: "状态", component: { name: "cl-switch" }, minWidth: 100 },
        { prop: "description", label: "描述", showOverflowTooltip: true, minWidth: 200 },
        { prop: "createTime", label: "创建时间", sortable: "desc", minWidth: 160 },
        { prop: "updateTime", label: "更新时间", sortable: "custom", minWidth: 160 },
        { type: "op", buttons: ["edit", "delete"] }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        service: service.app.version
    },
    (app) => {
        app.refresh();
    }
);
</script>
src/modules/base/static/css/animation.scss
New file
@@ -0,0 +1,29 @@
@keyframes dou {
    0% {
        transform: rotate(0);
    }
    11% {
        transform: rotate(7.61deg);
    }
    23% {
        transform: rotate(-5.8deg);
    }
    36% {
        transform: rotate(3.35deg);
    }
    49% {
        transform: rotate(-1.9deg);
    }
    62% {
        transform: rotate(1.12deg);
    }
    75% {
        transform: rotate(-0.64deg);
    }
    88% {
        transform: rotate(0.37deg);
    }
    100% {
        transform: rotate(-0.28deg);
    }
}
src/modules/base/static/svg/icon-command.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441314533" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9317" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M478.176 454.848c-17.12 0-33.184 6.656-45.248 18.72-24.928 24.96-24.928 65.568 0 90.592 24.128 24.096 66.304 24.064 90.496-0.032 12.096-12.096 18.752-28.16 18.752-45.28s-6.656-33.184-18.752-45.28c-12.064-12.096-28.096-18.72-45.248-18.72zM512 63.968c-247.04 0-448 172.256-448 384 0 116.48 63.008 226.048 170.592 298.944l87.328 71.52c-3.904 19.328-17.408 60.096-32.64 97.536-4.928 12.096-1.984 25.984 7.36 35.072a32.049 32.049 0 0 0 22.272 8.992c4.384 0 8.8-0.896 12.992-2.752 36.416-16.224 147.488-68.96 187.584-125.376C763.104 828.448 960 657.568 960 447.968c0-211.744-200.96-384-448-384z m214.624 334.656c12.512 12.512 12.512 32.736 0 45.248-6.24 6.24-14.432 9.376-22.624 9.376s-16.384-3.136-22.624-9.376l-39.84-39.84-52.288 52.288c10.688 18.944 16.896 40.128 16.928 62.496 0.032 34.24-13.312 66.4-37.504 90.592-24.16 24.16-56.288 37.472-90.496 37.472-34.176 0-66.336-13.312-90.496-37.472-49.888-49.984-49.888-131.2 0-181.056 24.16-24.16 56.288-37.472 90.496-37.472 24.032 0 46.912 6.912 66.848 19.2l136.352-136.352c12.512-12.512 32.736-12.512 45.248 0s12.512 32.736 0 45.248l-39.84 39.84 39.84 39.808z" p-id="9318"></path></svg>
src/modules/base/static/svg/icon-fx.svg
New file
@@ -0,0 +1,29 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg
    t="1695872084016"
    class="icon"
    viewBox="0 0 1024 1024"
    version="1.1"
    xmlns="http://www.w3.org/2000/svg"
    p-id="3043"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    width="64"
    height="64"
>
    <path
        d="M853.333333 85.333333H170.666667a86.570667 86.570667 0 0 0-85.333334 85.333334v682.666666a85.717333 85.717333 0 0 0 85.333334 85.333334h682.666666a86.186667 86.186667 0 0 0 85.333334-85.333334V170.666667a85.333333 85.333333 0 0 0-85.333334-85.333334zM170.666667 853.333333V170.666667h682.666666v682.666666z"
        p-id="3044"
    ></path>
    <path
        d="M387.2 379.733333a61.781333 61.781333 0 0 1 61.781333-61.781333h19.114667a30.890667 30.890667 0 0 0 0-61.781333h-19.114667a123.733333 123.733333 0 0 0-123.733333 123.733333v304.512a30.890667 30.890667 0 1 0 61.781333 0z"
        p-id="3045"
    ></path>
    <path
        d="M261.76 429.610667h217.728a27.392 27.392 0 0 1 31.146667 30.890666 27.392 27.392 0 0 1-31.146667 30.890667h-217.6a27.434667 27.434667 0 0 1-31.146667-30.890667 27.392 27.392 0 0 1 31.146667-30.890666z"
        p-id="3046"
    ></path>
    <path
        d="M737.322667 331.008a30.890667 30.890667 0 0 0-26.88 15.445333l-61.781334 108.117334-61.781333-108.117334a30.890667 30.890667 0 1 0-53.461333 30.890667l80.341333 139.008-80.341333 139.008a30.890667 30.890667 0 1 0 53.461333 30.890667l61.781333-108.117334 61.781334 108.117334a30.890667 30.890667 0 1 0 53.461333-30.890667l-80.341333-139.008 80.341333-139.008a30.890667 30.890667 0 0 0-26.581333-46.336z"
        p-id="3047"
    ></path>
</svg>
src/modules/base/static/svg/icon-living.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441325978" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10297" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M407.467 488.533a106.667 106.667 0 1 0 213.333 0 106.667 106.667 0 1 0-213.333 0z" p-id="10298"></path><path d="M512 64C264.533 64 64 264.533 64 512c0 236.8 183.467 428.8 416 445.867v-134.4c-53.333-59.734-200.533-230.4-200.533-334.934 0-130.133 104.533-234.666 234.666-234.666S748.8 358.4 748.8 488.533c0 61.867-49.067 153.6-145.067 270.934L544 827.733V960c232.533-17.067 416-211.2 416-448 0-247.467-200.533-448-448-448z" p-id="10299"></path></svg>
src/modules/base/static/svg/icon-message.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441267509" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5815" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512 67.168c-247.04 0-448 172.256-448 384 0 116.512 63.04 226.048 174.048 301.408l84.16 52.032c-3.68 23.552-18.752 74.496-33.44 115.936-4.256 12.064-0.928 25.504 8.448 34.176a32.006 32.006 0 0 0 21.728 8.512c4.416 0 8.832-0.896 13.024-2.752 36.416-16.224 147.456-68.96 187.584-125.376C763.136 831.616 960 660.704 960 451.168c0-211.744-200.96-384-448-384z m68.224 476.8H384.96c-17.664 0-32-14.336-32-32s14.336-32 32-32h195.264c17.696 0 32 14.336 32 32s-14.304 32-32 32z m60.704-128H384.96c-17.664 0-32-14.336-32-32s14.336-32 32-32h255.968c17.696 0 32 14.336 32 32s-14.304 32-32 32z" p-id="5816"></path></svg>
src/modules/base/static/svg/icon-new.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441271889" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6095" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512 63.68c-247.04 0-448 172.256-448 384 0 116.48 63.008 226.048 170.592 298.944l87.328 71.52c-3.904 19.328-17.408 60.096-32.64 97.536-4.928 12.096-1.984 25.984 7.36 35.072a32.049 32.049 0 0 0 22.272 8.992c4.384 0 8.8-0.896 12.992-2.752 36.416-16.224 147.488-68.96 187.616-125.376C763.136 828.16 960 657.248 960 447.68c0-211.712-200.96-384-448-384z m219.136 342.88l-128.8 188.864c-5.984 8.736-15.872 13.984-26.432 13.984h-0.448c-10.72-0.16-20.672-5.664-26.464-14.688l-91.584-142.208L385.824 592c-5.664 11.072-16.864 17.408-28.512 17.408-4.896 0-9.92-1.12-14.592-3.52-15.712-8.064-21.92-27.328-13.856-43.072L425.76 373.92a31.925 31.925 0 0 1 26.912-17.344c11.52-0.416 22.272 5.024 28.48 14.656l95.456 148.288L678.24 370.528c9.984-14.592 29.856-18.336 44.48-8.416 14.592 9.92 18.368 29.856 8.416 44.448z" p-id="6096"></path></svg>
src/modules/base/static/svg/icon-pending.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441252941" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4832" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M736 96c31.04 0 51.712 25.856 51.712 62.08v304.96c-129.28 0-232.64 103.36-227.456 232.576 0 113.728 77.504 206.784 180.928 227.456H115.712c-31.04 0-51.712-25.856-51.712-67.2V158.08c0-36.16 20.672-62.016 51.712-62.016z m43.072 413.568l10.88 0.32C883.328 515.392 960 591.872 960 687.808c0 99.648-82.688 183.552-180.928 183.552h-5.12a179.2 179.2 0 0 1-124.16-52.416c-10.24-10.496-15.424-15.744-20.608-26.24a5.888 5.888 0 0 0-0.832-3.072l-3.52-4.288-0.832-3.136a16 16 0 0 1-3.072-4.736l-4.224-11.52-3.072-4.672c0-5.248-5.12-10.496-5.12-15.744l-1.344-5.248c-1.536-3.52-3.84-7.04-3.84-10.496-5.12-10.496-5.12-26.24-5.12-36.736 0-26.24 5.12-47.168 15.424-68.16v-5.248c0-5.248 5.184-15.744 10.368-20.928 0-1.344 0.32-2.304 0.832-3.2l3.52-4.224 0.832-3.072c30.976-47.232 82.688-78.72 149.888-78.72z m0 52.416c-15.488 0-25.856 10.496-25.856 26.24v125.824c0 15.744 10.368 26.24 25.856 26.24H892.8c10.368 0 25.856-10.496 25.856-26.24 0-15.744-10.368-26.24-25.856-26.24h-87.872v-99.584c0-15.744-10.368-26.24-25.856-26.24zM394.88 468.16H198.4a40.768 40.768 0 0 0-41.344 41.408c0 20.672 15.488 41.344 36.16 41.344h196.48c25.792 0 46.464-15.552 46.464-41.344a40.768 40.768 0 0 0-41.344-41.408z m165.44-206.72H198.4a40.768 40.768 0 0 0-41.344 41.344c0 20.672 15.488 41.344 36.16 41.344h361.856a40.768 40.768 0 0 0 41.344-41.344c0-25.856-15.488-41.344-36.16-41.344z" p-id="4833"></path></svg>
src/modules/base/static/svg/icon-scan.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441290031" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7496" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M878.05658706 550.17593363c-3.09394647 0.78966835-6.93872514 0.86734064-10.61521382 0.86734064-101.25878361 0.05178153-202.53051259 0.05178153-303.78929621 0.03883615l-146.10158933 0.01294539c-87.48489639-0.01294539-174.95684741 0-262.45468918-0.01294539-20.49254083 0-38.5772405-14.52471941-41.15337166-33.07545284-3.21045491-22.9262728 8.15559111-41.71002311 28.94587575-47.82024375 3.90950558-1.15213906 8.78991486-1.70879052 14.90013552-1.70879052 90.61767902-0.03883615 181.23535803-0.03883615 271.85303704-0.05178153 142.81346213 0 290.48144276-0.01294539 435.74158222-0.16828997h0.06472691c29.29540109 0 42.3184561 20.02650706 44.55800731 37.16619377 2.64085808 20.1430155-11.10713837 39.37985422-31.94920455 44.75218805z m85.53014361 185.79213274c0 32.86832672-0.9061768 89.06423309-4.00012326 107.73147496-4.50499318 27.25003061-19.10738489 54.20231743-41.11453551 75.89877886-22.0977683 21.77413373-49.30896276 35.89754627-76.61077492 39.75527033-22.90038203 3.24929106-46.97879387 3.46936257-70.26753737 3.67648868-8.33682647 0.06472691-16.97139675 0.1423992-25.4635678 0.36247072-11.72851675 0.28479842-24.20786569 0.29774381-36.0399455-1.59228207-21.82591525-3.49525333-33.70977659-24.98458864-32.22105756-42.90099832 1.82529897-21.90358755 19.39218331-38.53840435 40.88151861-38.69374894 5.57945995-0.03883615 11.14597452-0.09061769 16.72543447-0.1423992 11.36604603-0.10356306 22.90038203-0.2200715 34.49944493-0.22007152 12.09098747 0 24.23375645 0.11650845 36.23412622 0.47897916 19.98767091 0.60843298 38.86203891-7.21057817 53.28319527-22.00715061 14.17519408-14.5376648 22.020096-34.27937343 20.98446538-52.81716148-1.17802983-21.15275536-0.86734064-42.95277985-0.55665146-64.02786291 0.11650845-7.2623597 0.2200715-14.78362706 0.27185303-22.18838598 0.1423992-23.18518045 13.32079882-39.74232494 35.23733177-44.28615427 10.7058315-2.22660583 22.74503743 0.93206755 32.23400295 8.44038953 9.83849086 7.78017502 15.43089619 19.10738489 15.76747616 31.88447763 0.09061769 4.22019476 0.15534459 11.54728139 0.15534459 20.64788543zM344.74565531 920.68573235c0.02589076 25.56713086-17.19146825 42.78448987-42.81038064 42.82332602-28.41511506 0.05178153-56.57132247 0.03883615-87.79558558 0.01294538-36.41536158-0.02589076-71.96338252-13.38552573-100.08075378-37.5933914-28.22093431-24.29848336-46.47392395-57.4516085-51.40611478-93.36210016-2.62791269-19.04265797-2.33016889-85.91850509-2.21366043-111.08432908 0.01294539-3.10689185 0.02589076-5.67007763 0.02589076-7.50832197 0.03883615-21.08802845 16.12994686-37.748736 38.27949669-39.63876189 1.00973985-0.09061769 2.04537047-0.12945383 3.0680557-0.12945382 15.34027852 0 33.10134361 9.50191091 38.56429512 31.52200692 1.52755517 6.13611141 1.56639131 13.08778192 1.61817283 20.4407593l0.01294539 1.69584515c0.11650845 14.21403022 0.10356306 28.6869681 0.0776723 42.69387218-0.01294539 12.582912-0.03883615 25.60596702 0.05178153 38.44778667 0.24596227 39.45752652 31.26309925 70.68178963 70.59117195 71.0830965 20.0653432 0.19418075 40.44137561 0.15534459 60.1442481 0.11650844 9.59252859-0.02589076 19.15916642-0.03883615 28.73874963-0.02589076 24.97164325 0 43.12106983 17.04906903 43.13401521 40.50610252z m0-818.14818766c-0.01294539 22.65441975-16.95845136 39.75527031-39.405745 39.75527033-9.45012939 0.01294539-18.874368 0-28.311552-0.01294539-18.58956958-0.03883615-37.80051753-0.06472691-56.73961244 0.0647269-37.95586213 0.25890765-67.22537245 22.12365906-76.3907034 57.07619241-1.50166439 5.70891378-1.7217359 11.3919368-1.73468129 16.71248909-0.05178153 11.18481067-0.01294539 22.53791131 0.01294539 33.51559584 0.05178153 18.70607803 0.11650845 38.07237057-0.20712612 57.03735626-0.33657995 18.9520403-7.87079269 31.35371694-23.068672 37.91702597-17.35975822 7.4824312-31.97509531 6.00665758-44.66157037-4.54382934-8.95820483-7.45654045-13.57970647-17.12674133-13.7091603-28.75169501-0.33657995-29.01060267-0.23301689-109.2719755 4.09074094-132.00406756 5.41116997-28.47984197 21.12686459-55.96288948 44.20848197-77.40044325 22.95216355-21.30809995 51.74269472-35.35384019 81.07693195-39.57403497 7.68955733-1.10035753 15.17198853-1.68289975 22.24016751-1.72173589 24.80335328-0.16828997 50.03390419-0.12945383 74.448896-0.10356306l19.04265798 0.02589076c10.88706686 0 20.80323003 4.14252247 27.89729974 11.66378983 7.23646894 7.65072118 11.22364682 18.43422498 11.21070144 30.34397708z m618.80223921 184.04450607c-0.02589076 6.58919981-0.06472691 13.16545422-0.0776723 19.74170865-0.05178153 21.02330153-15.19787931 39.58698035-34.51239032 42.24078379-21.73529758 3.0033288-40.77795555-10.14918005-46.30563398-31.93625915-0.64726914-2.57613117-0.73788682-5.74774992-0.75083219-8.77696948-0.03883615-13.42436187-0.02589076-26.83577837-0.01294538-40.26014026 0-17.1526321 0.01294539-34.90075181-0.06472692-52.33818231-0.16828997-32.53174676-20.1430155-60.31253808-49.69732425-69.10245294-7.83195653-2.33016889-16.67365294-3.57292563-26.25323615-3.68943408-13.02305502-0.15534459-26.14967309-0.19418075-39.14683733-0.19418073-6.92577975 0-13.81272336 0.01294539-20.62199467 0.02589077-10.01972622 0.01294539-20.05239783 0.02589076-30.07212405 0.01294537-20.2983601-0.03883615-38.24066053-19.04265797-38.39600513-40.67439249-0.09061769-10.73172227 4.42732089-21.50228069 12.38873126-29.56725412 7.28825047-7.37886815 16.36296375-11.46960909 25.61891239-11.52139061 7.02934282-0.03883615 13.82566875-0.05178153 20.3760324-0.05178154 56.58426785 0 94.87670992 1.38515595 108.76710558 4.00012326 56.22179714 10.55048691 104.55985619 59.85944969 114.95499852 117.20749511 2.12304276 11.68968059 3.2881272 24.44088257 3.57292563 38.96560197 0.42719763 21.90358755 0.33657995 44.28615427 0.23301689 65.91788879z" p-id="7497"></path></svg>
src/modules/base/static/svg/icon-system.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1614441320870" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9877" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M956.17988281 446.79003906c-3.50683594-19.66376953-22.35234375-39.46289063-41.85175781-43.92597656l-14.45800781-3.34248047c-34.29140625-10.35351563-64.74550781-33.27539063-84.08232422-66.74589844-19.49941406-33.71132813-23.93789063-72-15.49863281-107.05693359l4.27148437-13.72060547c5.86230469-19.08984375-1.75253906-45.38144531-16.92421875-58.31103515 0 0-13.74873047-11.61035156-52.47949219-34.01367188-38.72460938-22.37519531-55.53896484-28.48535156-55.53896484-28.48535156-18.78925781-6.79130859-45.30146484-0.32607422-58.88408203 14.35253906l-10.19003906 10.95380859c-26.07363281 24.64892578-61.21142578 39.740625-99.83056641 39.740625-38.80546875 0-74.05488281-15.20068359-100.15488281-40.01396484l-9.83320313-10.62773437C387.1953125 90.94199219 360.65498047 84.44951172 341.86748047 91.24433594c0 0-16.81523438 6.07851562-55.62597656 28.48095703-38.66923828 22.40419922-52.33623047 33.9609375-52.33623047 33.9609375-15.22792969 12.92871094-22.86914063 39.11132813-16.95410156 58.28291016l4.24423828 13.85947265c8.32763672 35.02792969 3.83554687 73.23574219-15.49863282 106.92246094-19.39130859 33.60585937-50.146875 56.58398438-84.57539062 66.82324219l-14.1328125 3.23261718c-19.41503906 4.43847656-38.28955078 24.26572266-41.79550781 43.90136719 0 0-3.17548828 17.66601563-3.17548829 62.49902344 0 44.83564453 3.17548828 62.41992188 3.17548829 62.41992187 3.53320313 19.71826172 22.37871094 39.49189453 41.7946289 44.03759766l13.80761719 3.06914063c34.56298828 10.29550781 65.39941406 33.35712891 84.90234375 67.09921875 19.41503906 33.68671875 23.85351563 71.97539063 15.44414062 107.03320312l-4.18974609 13.69335938c-5.91503906 19.06083984 1.72529297 45.3515625 17.00683594 58.27939453 0 0 13.66699219 11.61298828 52.42148437 34.01542968 38.75361328 22.4578125 55.51611328 28.48623047 55.51611328 28.48623047 18.815625 6.79130859 45.27158203 0.27421875 58.85771485-14.29980468l9.72246094-10.45986329c26.10087891-24.92226562 61.37402344-40.09394531 100.23662109-40.09394531 38.83886719 0 74.16914062 15.28154297 100.37988281 40.09394531h0.02460938l9.61523437 10.46074219c13.58261719 14.57226563 39.98408203 21.09023438 58.82519531 14.29980469 0 0 16.7625-6.13476563 55.65410157-28.48710938 38.67363281-22.40332031 52.36523438-34.01542969 52.36523437-34.01542968 15.2296875-12.92695313 22.78828125-39.10869141 16.98310547-58.28027344l-4.27236328-14.1328125c-8.21777344-34.94619141-3.7265625-73.07226563 15.49863281-106.59287109 19.55566406-33.79658203 50.34111328-56.75097656 84.84785156-67.09921875v-0.05712891l13.80410157-3.17460938c19.50029297-4.43935547 38.34580078-24.21035156 41.84912109-43.93125 0 0 3.17636719-17.63789062 3.17636719-62.49902343-0.10898438-44.64316406-3.28447266-62.28105469-3.28447266-62.28105469z m-445.49121093 207.05449219c-79.61923828 0-144.20039063-64.74462891-144.20039063-144.60820312 0-79.75371094 64.58027344-144.44208984 144.20039063-144.44208985 79.61484375 0 144.11425781 64.71650391 144.11425781 144.55195313 0 79.86357422-64.49941406 144.49833984-144.11425781 144.49833984z" p-id="9878"></path></svg>
src/modules/base/static/svg/icon-theme.svg
New file
@@ -0,0 +1,12 @@
<svg t="1615283148941" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5045"
    width="64" height="64">
    <path
        d="M552.75 563.239c44.4 26.348 99.1 26.348 143.5 0C740.65 536.89 768 488.197 768 435.5c0-81.462-64.247-147.5-143.5-147.5S481 354.038 481 435.5c0 52.697 27.35 101.39 71.75 127.739z"
        p-id="5046"></path>
    <path
        d="M471.264 952c-18.842 0-38.581-1.796-60.115-4.49l-5.384-0.899c-152.533-23.352-256.614-171.55-261.1-177.837-147.149-224.541-73.574-451.777 65.5-577.52C348.34 65.511 578.934 11.621 780.814 174.19c130.102 105.085 169.58 250.588 171.375 256.875v1.797c18.843 102.39 3.59 177.836-45.76 226.337-75.368 72.752-200.086 49.4-217.134 45.807-23.328-2.695-40.376 4.49-52.938 19.76-13.458 17.064-16.15 39.518-11.664 52.991 12.562 37.723 14.356 66.464 5.384 88.02C604.058 922.361 551.12 952 471.264 952z m-51.973-80.488l5.083 0.849c82.179 12.72 132.164-3.393 152.497-49.188 0-0.849 4.236-11.873-6.778-45.796-11.013-30.53-2.541-70.39 19.486-97.528 22.875-28.834 56.763-41.556 96.581-37.315l2.542 0.848c0.847 0 105.9 22.898 160.122-29.683 33.04-32.226 43.207-88.199 28.805-166.221-3.39-11.025-39.819-130.603-144.872-214.562-166.053-135.69-356.674-90.743-471.894 13.57-105.9 96.68-183.843 280.71-53.374 480.007 0.848 0 89.804 127.21 211.802 145.02z"
        p-id="5047"></path>
    <path
        d="M203 529c0-29.823 24.177-54 54-54s54 24.177 54 54-24.177 54-54 54-54-24.177-54-54z m90-115.235A54 54 0 0 1 266 367c0-29.823 24.177-54 54-54s54 24.177 54 54a54 54 0 0 1-81 46.765zM427 278c0-29.823 24.177-54 54-54s54 24.177 54 54-24.177 54-54 54-54-24.177-54-54z m180 44c0-29.823 24.177-54 54-54s54 24.177 54 54-24.177 54-54 54-54-24.177-54-54z m90 144c0-29.823 24.177-54 54-54s54 24.177 54 54-24.177 54-54 54-54-24.177-54-54z"
        p-id="5048"></path>
</svg>
src/modules/base/static/svg/icon-track.svg
New file
@@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1676620705733" class="icon" viewBox="0 0 1024 1024" version="1.1"
    xmlns="http://www.w3.org/2000/svg" p-id="1701" xmlns:xlink="http://www.w3.org/1999/xlink"
    width="64" height="64">
    <path
        d="M823.04 746.24H200.96c-15.36 0-28.8-11.52-28.8-28.8v-11.52c0-15.36 11.52-28.8 28.8-28.8h120.96c19.2 0 36.48-15.36 36.48-36.48s-15.36-32.64-34.56-32.64H200.96c-55.68 0-99.84 44.16-99.84 99.84v11.52c0 55.68 44.16 99.84 99.84 99.84h620.16c15.36 0 28.8 11.52 28.8 28.8v9.6c0 15.36-11.52 28.8-28.8 28.8H308.48c-19.2 0-36.48 15.36-36.48 36.48 0 19.2 15.36 36.48 36.48 36.48h512.64c55.68 0 99.84-44.16 99.84-99.84v-11.52c1.92-57.6-44.16-101.76-97.92-101.76z"
        p-id="1702"></path>
    <path
        d="M512 66.56c-128.64 0-232.32 103.68-232.32 232.32S437.12 654.08 512 654.08c96 0 232.32-226.56 232.32-355.2S638.72 66.56 512 66.56zM512 416c-65.28 0-119.04-53.76-119.04-119.04s53.76-119.04 119.04-119.04 119.04 53.76 119.04 119.04S577.28 416 512 416z"
        p-id="1703"></path>
</svg>
src/modules/base/static/svg/icon-upload.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1695871553941" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1956" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M155.09375 385.15625H92.1875v453c0 34.78125 28.40625 63.09375 62.90625 63.09375H868.25c34.6875 0 62.90625-28.40625 62.90625-63.09375V385.15625H155.09375z m720.28125-153.84375H780.3125c-1.875 0-3.75 0.09375-5.71875 0.28125h-286.875v-51.1875c0-31.125-25.21875-56.34375-56.25-56.34375H148.71875c-31.125 0-56.25 25.3125-56.25 56.34375v163.125h838.96875V287.375c-0.09375-30.84375-25.3125-56.0625-56.0625-56.0625z" fill="" p-id="1957"></path></svg>
src/modules/cs/components/index.vue
New file
@@ -0,0 +1,186 @@
<template>
    <div class="cs-chat__wrap">
        <el-badge
            :value="message.unread"
            :hidden="!message.unread"
            v-permission="service.cs.msg.permission.unreadCount"
        >
            <div class="cs-chat__icon" @click="open">
                <cl-svg name="icon-notice" :size="16" />
            </div>
        </el-badge>
        <!-- 弹框 -->
        <cl-dialog
            v-model="visible"
            title="客服会话"
            height="700px"
            width="1200px"
            padding="0"
            keep-alive
            :scrollbar="false"
            :close-on-click-modal="false"
            :close-on-press-escape="false"
            :controls="['slot-expand', 'cl-flex1', 'fullscreen', 'close']"
            @close="onClose"
        >
            <div
                class="cs-chat"
                :class="{
                    'is-mini': browser.isMini,
                    'is-expand': tools.isExpand
                }"
            >
                <div class="cs-chat__left">
                    <cs-session />
                </div>
                <div class="cs-chat__right">
                    <cs-message />
                </div>
            </div>
            <!-- 展开按钮 -->
            <template #slot-expand>
                <button class="cl-dialog__controls-icon">
                    <el-icon @click="tools.expand(true)" v-if="!tools.isExpand">
                        <notebook />
                    </el-icon>
                    <el-icon @click="tools.expand(false)" v-else>
                        <arrow-left />
                    </el-icon>
                </button>
            </template>
        </cl-dialog>
    </div>
</template>
<script lang="tsx" name="cs-chat" setup>
import { onMounted, ref } from 'vue';
import { useBrowser, useCool } from '/@/cool';
import { Notebook, ArrowLeft } from '@element-plus/icons-vue';
import { useMessage, useSession, useSocket, useTools, useNotice } from '../hooks';
import CsMessage from './message.vue';
import CsSession from './session.vue';
import type { Cs } from '../types';
const { mitt, service } = useCool();
const { browser } = useBrowser();
const socket = useSocket();
const session = useSession();
const message = useMessage();
const tools = useTools();
const notice = useNotice();
// 是否可见
const visible = ref(false);
// 打开
async function open() {
    visible.value = true;
    // 刷新会话
    await session.refresh();
}
// 关闭
function close() {
    visible.value = false;
}
// 关闭
function onClose() {
    message.getUnread();
}
onMounted(() => {
    if (service.cs.msg._permission.unreadCount) {
        // 连接 socket
        socket.connect();
        // 获取未读消息
        message.getUnread();
        // 监听消息
        mitt.on('cs.msg', (data: Cs.Msg) => {
            if (data.sessionId == session.info?.id) {
                message.read(data.id!);
            } else {
                message.unread += 1;
            }
            // 消息通知
            if (!visible.value) {
                notice.create(data, {
                    open
                });
            }
        });
    }
});
defineExpose({
    open,
    close
});
</script>
<style lang="scss">
.cs-chat {
    display: flex;
    justify-content: flex-end;
    background-color: #eee;
    padding: 5px;
    box-sizing: border-box;
    position: relative;
    color: #000;
    height: 100%;
    &__icon {
        padding: 0 5px;
        text-align: center;
        &:hover {
            color: var(--color-primary);
        }
    }
    &__footer {
        padding: 9px 0;
    }
    &__left {
        height: calc(100% - 10px);
        width: 345px;
        position: absolute;
        left: 5px;
        top: 5px;
    }
    &__right {
        position: relative;
        z-index: 99;
        transition: width 0.3s;
        width: 100%;
    }
    &.is-mini {
        .cs-chat__right {
            width: 100% !important;
        }
        &.is-expand {
            .cs-chat__left {
                width: calc(100% - 10px);
                z-index: 100;
            }
        }
    }
    &.is-expand {
        .cs-chat__right {
            width: calc(100% - 350px);
        }
    }
}
</style>
src/modules/cs/components/message.vue
New file
@@ -0,0 +1,526 @@
<template>
    <div
        class="cs-message"
        v-loading="message.loading"
        element-loading-text="加载中"
        v-if="session.info"
    >
        <!-- 头部 -->
        <div class="head">
            <div class="avatar">
                <cl-avatar :size="30" shape="square" :src="session.info?.avatarUrl" />
            </div>
            <span class="name">与“{{ session.info?.nickName }}”聊天中</span>
        </div>
        <!-- 消息列表 -->
        <el-scrollbar class="scrollbar" :ref="setRefs('scrollbar')" @scroll="onScroll">
            <div class="list">
                <ul
                    v-for="(d, n) in message.list"
                    :key="d.id"
                    :class="{
                        'is-show': message.list.length - n <= pagination.page
                    }"
                >
                    <li v-for="(item, index) in d.data" :key="index">
                        <div class="date" v-if="item.date">
                            <span>{{ item.date }}</span>
                        </div>
                        <div
                            class="item"
                            :class="{
                                'is-right': item.type == 1
                            }"
                        >
                            <div class="avatar">
                                <cl-avatar :size="36" shape="square" :src="item.avatarUrl" />
                            </div>
                            <div
                                class="det"
                                @contextmenu="
                                    (e) => {
                                        onContextMenu(e, item);
                                    }
                                "
                            >
                                <div class="h">
                                    <span class="name">{{ item.nickName }}</span>
                                </div>
                                <div class="content">
                                    <!-- 文本 -->
                                    <div class="text" v-if="item.content.type == 'text'">
                                        <span>{{ item.content.data }}</span>
                                    </div>
                                    <!-- 图片 -->
                                    <div class="image" v-else-if="item.content.type == 'image'">
                                        <el-image
                                            :src="item.content.data"
                                            :preview-src-list="[item.content.data]"
                                            :initial-index="item._index"
                                            scroll-container=".cs-message .list"
                                        >
                                            <template #error>
                                                <div class="error">图片加载失败</div>
                                            </template>
                                        </el-image>
                                    </div>
                                    <!-- 表情 -->
                                    <div
                                        class="emoji text"
                                        v-else-if="item.content.type == 'emoji'"
                                    >
                                        <img :src="getEmoji(item.content.data)" />
                                    </div>
                                </div>
                            </div>
                        </div>
                    </li>
                </ul>
            </div>
            <div class="empty" v-if="message.list.length == 0">
                <el-empty :image-size="100" description="暂无消息" />
            </div>
        </el-scrollbar>
        <!-- 底部 -->
        <div class="footer">
            <div class="tools">
                <ul>
                    <cl-upload
                        type="file"
                        :show-file-list="false"
                        accept="image/*"
                        @success="onImageSend"
                    >
                        <li>
                            <el-icon>
                                <icon-picture />
                            </el-icon>
                        </li>
                    </cl-upload>
                    <li @click="refs.uploadSpace.open()">
                        <el-icon>
                            <folder />
                        </el-icon>
                    </li>
                    <tools-emoji>
                        <li>
                            <el-icon>
                                <star />
                            </el-icon>
                        </li>
                    </tools-emoji>
                </ul>
            </div>
            <div class="input">
                <el-input
                    v-model="content"
                    type="textarea"
                    placeholder="请输入..."
                    :rows="4"
                    resize="none"
                    :autosize="{
                        minRows: 4,
                        maxRows: 10
                    }"
                    @keydown="onKeydown"
                />
                <el-button type="success" :disabled="!content" @click="onTextSend">发送</el-button>
            </div>
        </div>
        <!-- 文件空间 -->
        <cl-upload-space
            :ref="setRefs('uploadSpace')"
            :show-btn="false"
            accept="image/*"
            @confirm="onFileConfirm"
        />
    </div>
</template>
<script lang="ts" setup>
import { nextTick, reactive, ref, watch } from "vue";
import { Picture as IconPicture, Folder, Star } from "@element-plus/icons-vue";
import { ContextMenu } from "@cool-vue/crud";
import { useClipboard } from "@vueuse/core";
import { ElMessage } from "element-plus";
import { useSession, useSocket, useMessage } from "../hooks";
import { useCool } from "/@/cool";
import ToolsEmoji from "./tools/emoji.vue";
import { getEmoji } from "../utils";
import type { Cs } from "../types";
const { refs, setRefs } = useCool();
const { copy } = useClipboard();
const socket = useSocket();
const session = useSession();
const message = useMessage();
// 分页
const pagination = reactive({
    page: 1
});
// 输入
const content = ref("");
// 加载中
const loading = ref(false);
// 按下
function onKeydown(e: KeyboardEvent) {
    if (e.key == "Enter" && !e.shiftKey) {
        onTextSend();
        e.preventDefault();
    }
}
// 文本消息
function onTextSend() {
    if (content.value) {
        socket.send({
            type: "text",
            data: content.value
        });
        content.value = "";
    }
}
// 图片消息
function onImageSend(res: { url: string }) {
    socket.send({
        type: "image",
        data: res.url
    });
}
// 右键菜单
function onContextMenu(e: Event, item: Cs.Msg) {
    ContextMenu.open(e, {
        hover: {
            target: "content"
        },
        list: [
            {
                label: "复制",
                callback(done) {
                    copy(item.content.data || "");
                    ElMessage.success("复制成功");
                    done();
                }
            }
        ]
    });
}
// 文件选择
function onFileConfirm(selection: any[]) {
    selection.forEach(onImageSend);
}
// 获取高度
function getHeight(): Promise<number> {
    return new Promise((resolve) => {
        resolve(document.querySelector(".cs-message .list")?.clientHeight || 0);
    });
}
// 滚动到指定位置
function scrollTo(top: number, smooth?: string) {
    nextTick(() => {
        refs.scrollbar?.wrapRef?.scroll({
            top,
            behavior: smooth ? "smooth" : "auto"
        });
    });
}
// 滚动到底部
function scrollToBottom() {
    scrollTo(99999 + Math.random(), "smooth");
}
// 滚动到顶部
async function scrollToUpper() {
    if (pagination.page > message.list.length) {
        return false;
    }
    loading.value = true;
    // 计算当前的高度
    const h1 = await getHeight();
    // 预加载下一页
    await message.refresh({
        page: pagination.page + 1
    });
    // 下一页
    pagination.page += 1;
    // 延迟
    await nextTick();
    // 计算页数增加后的高度
    const h2 = await getHeight();
    // 滚动到指定位置
    scrollTo(h2 - h1);
    loading.value = false;
}
// 滚动事件
function onScroll(e: { scrollTop: number }) {
    if (e.scrollTop == 0) {
        scrollToUpper();
    }
}
function toDev() {
    ElMessage.info("暂未开放");
}
watch(
    () => session.info?.id,
    async () => {
        // 重置分页
        pagination.page = 1;
        // 获取未读数
        message.getUnread();
        // 获取消息列表
        await message.refresh(pagination);
        // 滚动到底
        scrollToBottom();
    }
);
</script>
<style lang="scss" scoped>
.cs-message {
    display: flex;
    flex-direction: column;
    background-color: #fff;
    border-radius: 6px;
    height: 100%;
    box-sizing: border-box;
    .head {
        display: flex;
        align-items: center;
        height: 50px;
        padding: 0 10px;
        .name {
            margin-left: 10px;
            font-size: 14px;
        }
        ul {
            li {
                list-style: none;
            }
        }
    }
    .scrollbar {
        flex: 1;
        background-color: #f7f7f7;
        .list {
            ul {
                opacity: 0;
                height: 0;
                overflow: hidden;
                transition: all 0.3s;
                &.is-show {
                    height: auto;
                    opacity: 1;
                }
                & > li {
                    list-style: none;
                    .date {
                        margin: 10px;
                        text-align: center;
                        span {
                            background-color: #c9c9c9;
                            padding: 5px;
                            border-radius: 4px;
                            color: #fff;
                            font-size: 12px;
                        }
                    }
                    .item {
                        display: flex;
                        padding: 10px;
                        .avatar {
                            margin-right: 10px;
                        }
                        .det {
                            .h {
                                display: flex;
                                align-items: center;
                                .name {
                                    font-size: 12px;
                                    color: #666;
                                }
                            }
                            .content {
                                display: flex;
                                margin-top: 5px;
                                .text {
                                    display: inline-block;
                                    background-color: #fff;
                                    padding: 8px;
                                    border-radius: 0 6px 6px 6px;
                                    max-width: 400px;
                                    font-size: 14px;
                                }
                                .image {
                                    .el-image {
                                        display: block;
                                        height: 200px;
                                        border-radius: 4px;
                                    }
                                    .error {
                                        display: flex;
                                        justify-content: center;
                                        align-items: center;
                                        height: 200px;
                                        width: 200px;
                                        background-color: #fff;
                                        border-radius: 8px;
                                        letter-spacing: 1px;
                                        line-height: 1;
                                        color: var(--el-color-danger);
                                        font-size: 13px;
                                    }
                                }
                                .emoji {
                                    img {
                                        display: block;
                                        height: 22px;
                                    }
                                }
                            }
                        }
                        &.is-right {
                            flex-direction: row-reverse;
                            .avatar {
                                margin-left: 10px;
                                margin-right: 0;
                            }
                            .det {
                                .h,
                                .content {
                                    flex-direction: row-reverse;
                                }
                                .content {
                                    .text {
                                        border-radius: 6px 0 6px 6px;
                                        background-color: var(--el-color-primary);
                                        color: #fff;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            :deep(.el-scrollbar__view) {
                height: 100%;
            }
            .empty {
                display: flex;
                align-items: center;
                justify-content: center;
                height: 100%;
            }
        }
    }
    .footer {
        padding: 10px;
        .tools {
            display: flex;
            margin-bottom: 10px;
            ul {
                display: flex;
                align-items: center;
                flex: 1;
                li {
                    height: 26px;
                    width: 26px;
                    border-radius: 4px;
                    margin-right: 10px;
                    list-style: none;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    cursor: pointer;
                    font-size: 18px;
                    position: relative;
                    &:hover {
                        background-color: #f7f7f7;
                    }
                }
            }
        }
        .input {
            display: flex;
            position: relative;
            :deep(.el-textarea__inner) {
                box-shadow: none;
                padding: 0 5px;
            }
            .el-button {
                margin-left: 10px;
                position: absolute;
                right: 0;
                bottom: 0;
            }
        }
    }
}
</style>
src/modules/cs/components/session.vue
New file
@@ -0,0 +1,223 @@
<template>
    <div class="cs-session">
        <div class="head">
            <el-input :prefix-icon="Search" v-model="keyWord" placeholder="搜索" clearable />
            <ul class="tools">
                <li @click="session.refresh()">
                    <el-icon><refresh /></el-icon>
                </li>
            </ul>
        </div>
        <div class="container" v-loading="session.loading">
            <el-scrollbar class="scroller" :ref="setRefs('scroller')">
                <div
                    class="list"
                    v-infinite-scroll="onMore"
                    :infinite-scroll-immediate="false"
                    :infinite-scroll-disabled="session.loaded"
                >
                    <div
                        class="item"
                        v-for="(item, index) in flist"
                        :key="index"
                        :class="{
                            'is-active': item.id == session.info?.id
                        }"
                        @click="toDetail(item)"
                    >
                        <div class="avatar">
                            <el-badge
                                :value="item.adminUnreadCount"
                                :hidden="item.adminUnreadCount == 0"
                            >
                                <cl-avatar shape="square" :src="item.avatarUrl" />
                            </el-badge>
                        </div>
                        <div class="det">
                            <p class="name">{{ item.nickName }}</p>
                            <p class="message">
                                {{ msgFormatter(item.lastMsg) }}
                            </p>
                        </div>
                        <div class="status">
                            <p class="date">{{ dateFormatter(item.updateTime) }}</p>
                        </div>
                    </div>
                </div>
                <el-empty v-if="flist.length == 0" :image-size="100" description="暂无会话" />
            </el-scrollbar>
        </div>
    </div>
</template>
<script lang="ts" setup>
import { computed, nextTick, ref } from "vue";
import { Refresh, Search } from "@element-plus/icons-vue";
import { useBrowser, useCool } from "/@/cool";
import { useDialog } from "@cool-vue/crud";
import { useSession, useTools } from "../hooks";
import { msgFormatter, dateFormatter } from "../utils/index";
const { browser } = useBrowser();
const { refs, setRefs } = useCool();
const session = useSession();
const tools = useTools();
useDialog({
    onFullscreen() {
        nextTick(() => {
            refs.scroller?.update();
        });
    }
});
// 关键字
const keyWord = ref("");
// 过滤列表
const flist = computed(() => session.list.filter((e) => e.nickName?.includes(keyWord.value)) || []);
// 会话详情
async function toDetail(item: Eps.CsSessionEntity) {
    // 设置为会话
    session.set(item);
    // 收起
    if (browser.isMini) {
        tools.expand(false);
    }
}
// 加载更多
function onMore() {
    session.refresh({
        page: session.pagination.page + 1
    });
}
</script>
<style lang="scss" scoped>
.cs-session {
    height: 100%;
    width: 100%;
    background-color: #fff;
    border-radius: 6px;
    .head {
        display: flex;
        border-bottom: 1px solid #f7f7f7;
        padding: 10px;
        :deep(.el-input) {
            height: 30px;
            .el-input__wrapper {
                background-color: #eee;
                box-shadow: none;
            }
        }
        .tools {
            display: inline-flex;
            align-items: center;
            li {
                height: 30px;
                width: 30px;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                margin-left: 10px;
                border-radius: 4px;
                background-color: #eee;
                color: #666;
                .el-icon {
                    font-size: 16px;
                }
                &:hover {
                    background-color: #ddd;
                }
            }
        }
    }
    .container {
        height: calc(100% - 51px);
        overflow: hidden;
        .scroller {
            height: 100%;
        }
        .list {
            .item {
                display: flex;
                padding: 15px 10px;
                cursor: pointer;
                .avatar {
                    margin-right: 10px;
                    :deep(.el-badge__content) {
                        transform: translateY(-50%) translateX(calc(100% - 5px)) scale(0.8) !important;
                    }
                }
                .det {
                    flex: 1;
                    .name,
                    .message {
                        overflow: hidden;
                        text-overflow: ellipsis;
                        display: -webkit-box;
                        -webkit-box-orient: vertical;
                        -webkit-line-clamp: 1;
                        width: 220px;
                        line-height: 1;
                    }
                    .name {
                        font-size: 14px;
                        margin: 2px 0 8px 0;
                    }
                    .message {
                        font-size: 12px;
                        color: #666;
                    }
                }
                .status {
                    display: flex;
                    flex-direction: column;
                    align-items: flex-end;
                    font-size: 12px;
                    flex-shrink: 0;
                    .date {
                        margin-bottom: 5px;
                        color: #999;
                    }
                }
                &.is-active {
                    background-color: #eee;
                }
                &:not(.is-active):hover {
                    background-color: #f7f7f7;
                }
            }
        }
    }
}
</style>
src/modules/cs/components/tools/emoji.vue
New file
@@ -0,0 +1,162 @@
<template>
    <el-popover
        :ref="setRefs('popover')"
        placement="top"
        :width="515"
        trigger="click"
        popper-class="tools-emoji__popover"
    >
        <template #reference>
            <slot></slot>
        </template>
        <div class="tools-emoji">
            <div class="item" v-for="item in list" :key="item" @click="check(item)">
                <img :src="getEmoji(item)" />
            </div>
        </div>
    </el-popover>
</template>
<script lang="ts" name="tools-emoji" setup>
import { useCool } from "/@/cool";
import { useSocket } from "../../hooks";
import { getEmoji } from "../../utils";
const { refs, setRefs } = useCool();
const socket = useSocket();
// 列表
const list = [
    "angry-face.png",
    "anguished-face.png",
    "astonished-face.png",
    "confounded-face.png",
    "confused-face.png",
    "crying-face.png",
    "disappointed-but-relieved-face.png",
    "disappointed-face.png",
    "dizzy-face.png",
    "drooling-face.png",
    "expressionless-face.png",
    "face-savouring-delicious-food.png",
    "face-screaming-in-fear.png",
    "face-throwing-a-kiss.png",
    "face-with-cold-sweat.png",
    "face-with-cowboy-hat.png",
    "face-with-finger-covering-closed-lips.png",
    "face-with-head-bandage.png",
    "face-with-look-of-triumph.png",
    "face-with-medical-mask.png",
    "face-with-monocle.png",
    "face-with-one-eyebrow-raised.png",
    "face-with-open-mouth-and-cold-sweat.png",
    "face-with-open-mouth-vomiting.png",
    "face-with-open-mouth.png",
    "face-with-party-horn-and-party-hat.png",
    "face-with-pleading-eyes.png",
    "face-with-rolling-eyes.png",
    "face-with-stuck-out-tongue-and-tightly-closed-eyes.png",
    "face-with-stuck-out-tongue-and-winking-eye.png",
    "face-with-stuck-out-tongue.png",
    "face-with-thermometer.png",
    "face-with-uneven-eyes-and-wavy-mouth.png",
    "face-without-mouth.png",
    "fearful-face.png",
    "flushed-face.png",
    "freezing-face.png",
    "frowning-face-with-open-mouth.png",
    "grimacing-face.png",
    "grinning-face-with-one-large-and-one-small-eye.png",
    "grinning-face-with-smiling-eyes.png",
    "grinning-face-with-star-eyes.png",
    "grinning-face.png",
    "hugging-face.png",
    "hushed-face.png",
    "imp.png",
    "kissing-face-with-closed-eyes.png",
    "kissing-face-with-smiling-eyes.png",
    "kissing-face.png",
    "loudly-crying-face.png",
    "lying-face.png",
    "money-mouth-face.png",
    "nauseated-face.png",
    "nerd-face.png",
    "neutral-face.png",
    "overheated-face.png",
    "pensive-face.png",
    "persevering-face.png",
    "pouting-face.png",
    "relieved-face.png",
    "rolling-on-the-floor-laughing.png",
    "serious-face-with-symbols-covering-mouth.png",
    "shocked-face-with-exploding-head.png",
    "sleeping-face.png",
    "sleepy-face.png",
    "slightly-frowning-face.png",
    "slightly-smiling-face.png",
    "smiling-face-with-halo.png",
    "smiling-face-with-heart-shaped-eyes.png",
    "smiling-face-with-horns.png",
    "smiling-face-with-open-mouth-and-smiling-eyes.png",
    "smiling-face-with-open-mouth-and-tightly-closed-eyes.png",
    "smiling-face-with-open-mouth.png",
    "smiling-face-with-smiling-eyes-and-hand-covering-mouth.png",
    "smiling-face-with-smiling-eyes-and-three-hearts.png",
    "smiling-face-with-smiling-eyes.png",
    "smiling-face-with-sunglasses.png",
    "smirking-face.png",
    "sneezing-face.png",
    "thinking-face.png",
    "tired-face.png",
    "upside-down-face.png",
    "weary-face.png",
    "white-frowning-face.png",
    "white-smiling-face.png",
    "winking-face.png",
    "worried-face.png",
    "zipper-mouth-face.png"
];
// 监听改变
function check(val: string) {
    socket.send({
        type: "emoji",
        data: val
    });
    refs.popover.hide();
}
</script>
<style lang="scss">
.tools-emoji__popover {
    max-width: calc(100% - 10px);
}
</style>
<style lang="scss" scoped>
.tools-emoji {
    display: flex;
    flex-wrap: wrap;
    padding-left: 5px;
    .item {
        height: 40px;
        width: 40px;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
        img {
            height: 20px;
        }
        &:hover {
            background-color: #f7f7f7;
            border-radius: 4px;
        }
    }
}
</style>
src/modules/cs/config.ts
New file
@@ -0,0 +1,10 @@
import type { ModuleConfig } from "/@/cool";
export default (): ModuleConfig => {
    return {
        toolbar: {
            order: 2,
            component: import("./components/index.vue")
        }
    };
};
src/modules/cs/hooks/index.ts
New file
@@ -0,0 +1,5 @@
export * from "./socket";
export * from "./session";
export * from "./message";
export * from "./tools";
export * from "./notice";
src/modules/cs/hooks/message.ts
New file
@@ -0,0 +1,128 @@
import { defineStore } from "pinia";
import { computed, ref } from "vue";
import type { Cs } from "../types";
import dayjs from "dayjs";
import { service } from "/@/cool";
import { isEmpty, last } from "lodash-es";
import { useSession } from "./session";
import { uuid } from "/@/cool/utils";
import { useTools } from "./tools";
import { dateFormatter } from "../utils";
import { useBase } from "/$/base";
export const useMessage = defineStore("cs.message", () => {
    const session = useSession();
    const tools = useTools();
    const { user } = useBase();
    // 加载中
    const loading = ref(false);
    // 未读消息数
    const unread = ref(0);
    // 所有页
    const data = ref<{ id: string; data: Cs.Msg[] }[]>([]);
    // 列表
    const list = computed(() => {
        data.value.forEach((e) => {
            if (e.data[0]) {
                let date = e.data[0].createTime;
                if (date) {
                    e.data.forEach((a, i) => {
                        const d = dateFormatter(a.createTime);
                        if (i == 0) {
                            a.date = d;
                        }
                        if (dayjs(a.createTime).subtract(10, "minute").isAfter(dayjs(date))) {
                            a.date = d;
                            date = a.createTime;
                        }
                    });
                }
            }
        });
        return data.value;
    });
    // 追加消息
    function append(item: Cs.Msg) {
        const list = last(data.value)?.data;
        if (list) {
            list.push({
                sessionId: session.info?.id,
                type: 1,
                nickName: user.info?.name,
                avatarUrl: user.info?.headImg,
                ...item
            });
            tools.scrollToBottom();
        }
    }
    // 获取消息
    async function refresh(params?: any) {
        loading.value = true;
        if (params.page == 1) {
            data.value = [];
        }
        await service.cs.msg
            .page({
                sessionId: session.info?.id,
                order: "createTime",
                sort: "desc",
                ...params
            })
            .then((res) => {
                if (isEmpty(data.value) || !isEmpty(res.list)) {
                    res.list.forEach((e) => {
                        if (e.type == 1) {
                            e.avatarUrl = e.adminUserHeadImg;
                            e.nickName = e.adminUserName;
                        }
                    });
                    data.value.unshift({
                        id: uuid(),
                        data: res.list.reverse() as any[]
                    });
                }
            });
        loading.value = false;
    }
    // 获取未读消息
    function getUnread() {
        service.cs.msg.unreadCount().then((res) => {
            unread.value = res;
        });
    }
    // 读消息
    function read(id: number) {
        service.cs.msg.read({
            msgIds: [id]
        });
    }
    return {
        data,
        list,
        loading,
        unread,
        append,
        refresh,
        read,
        getUnread
    };
});
src/modules/cs/hooks/notice.tsx
New file
@@ -0,0 +1,68 @@
import Ding from "/$/cs/static/audio/ding.wav";
import type { Cs } from "../types";
import { ElNotification } from "element-plus";
import { getEmoji } from "../utils";
import { useSession } from "./session";
export function useNotice() {
    const session = useSession();
    // ding
    const audio = new Audio(Ding);
    // 消息提示
    function create({ content, user, sessionId }: Cs.Msg, { open }: { open: () => Promise<void> }) {
        if (content) {
            const icon = <img src={user.avatarUrl} style={{ height: "32px", width: "32px" }} />;
            let message = content.data;
            switch (content.type) {
                case "image":
                    message = "[图片]";
                    break;
                case "voice":
                    message = "[语音]";
                    break;
                case "video":
                    message = "[视频]";
                    break;
                case "file":
                    message = "[文件]";
                    break;
                case "location":
                    message = "[位置]";
                    break;
                case "emoji":
                    message = (
                        <img src={getEmoji(message)} style={{ height: "22px", width: "22px" }} />
                    );
                    break;
            }
            ElNotification({
                title: user.nickName,
                message,
                icon,
                async onClick() {
                    await open();
                    // 切换会话
                    session.setById(sessionId!);
                }
            });
        }
        // 播放声音
        audio?.play();
    }
    return {
        create
    };
}
src/modules/cs/hooks/session.ts
New file
@@ -0,0 +1,97 @@
import { defineStore } from "pinia";
import { reactive, ref } from "vue";
import { Cs } from "../types";
import { service } from "/@/cool";
export const useSession = defineStore("cs.session", () => {
    // 详情
    const info = ref<Cs.Session>();
    // 列表
    const list = ref<Cs.Session[]>([]);
    // 是否加载完
    const loaded = ref(false);
    // 加载状态
    const loading = ref(false);
    // 分页
    const pagination = reactive({
        page: 1,
        size: 20
    });
    // 请求参数
    const reqParams = {
        order: "updateTime",
        sort: "desc"
    };
    // 刷新
    async function refresh(params?: any) {
        loading.value = true;
        // 合并参数
        Object.assign(reqParams, params);
        // 请求
        await service.cs.session
            .page({
                ...pagination,
                ...reqParams
            })
            .then((res) => {
                // 设置分页
                Object.assign(pagination, res.pagination);
                if (res.pagination.page == 1) {
                    list.value = res.list;
                } else {
                    list.value.push(...res.list);
                }
                // 是否加载完成
                loaded.value = res.pagination.total <= list.value.length;
                // 默认第一个
                if (!info.value) {
                    set(list.value[0]);
                }
            });
        loading.value = false;
    }
    // 设置会话
    function set(data?: Cs.Session) {
        if (data) {
            data.adminUnreadCount = 0;
        }
        info.value = data;
    }
    // 通过id设置会话
    function setById(sessionId: number) {
        const item = list.value.find((e) => e.id == sessionId);
        set(item);
    }
    // 清空会话
    function clear() {
        info.value = undefined;
    }
    return {
        info,
        list,
        set,
        setById,
        clear,
        pagination,
        loaded,
        loading,
        refresh
    };
});
src/modules/cs/hooks/socket.ts
New file
@@ -0,0 +1,95 @@
import type { Cs } from "../types";
import { defineStore } from "pinia";
import io, { type Socket } from "socket.io-client";
import { useSession } from "./session";
import { useBase } from "/$/base";
import { config } from "/@/config";
import { useMessage } from "./message";
import { useCool } from "/@/cool";
export const useSocket = defineStore("cs.socket", () => {
    const { mitt } = useCool();
    const { user } = useBase();
    const session = useSession();
    const message = useMessage();
    let client = undefined as Socket | undefined;
    function connect() {
        if (!user.token) {
            return false;
        }
        if (client) {
            client.disconnect();
            client = undefined;
        }
        if (!client) {
            client = io(config.host + "/cs", {
                transports: ["websocket", "polling"]
            });
            client.auth = {
                isAdmin: true,
                token: user.token
            };
            client.on("connect", () => {
                console.log("[cs] connect");
            });
            client.on("disconnect", () => {
                console.log("[cs] disconnect");
            });
            client.on("msg", (data: Cs.Msg) => {
                if ((data.type == 1 && data.userId != user.info?.id) || data.type == 0) {
                    // 判断是否是当前会话
                    if (data.sessionId == session.info?.id) {
                        message.append({
                            ...data.user,
                            ...data
                        });
                    } else {
                        // 设置其他会话信息
                        session.list.forEach((e) => {
                            if (e.id == data.sessionId) {
                                if (!e.adminUnreadCount) {
                                    e.adminUnreadCount = 0;
                                }
                                e.adminUnreadCount += 1;
                                e.lastMsg = data;
                            }
                        });
                    }
                    // 发送事件
                    mitt.emit("cs.msg", data);
                }
            });
        }
    }
    function send(content: Cs.Content) {
        if (client) {
            // 发送事件
            client.emit("send", {
                sessionId: session.info?.id,
                content
            });
            // 追加消息
            message.append({ content });
        } else {
            console.log("[cs] client error");
        }
    }
    return {
        connect,
        client,
        send
    };
});
src/modules/cs/hooks/tools.ts
New file
@@ -0,0 +1,28 @@
import { defineStore } from "pinia";
import { nextTick, ref } from "vue";
export const useTools = defineStore("cs.tools", () => {
    // 是否展开
    const isExpand = ref(true);
    // 收起、展开
    function expand(value?: boolean) {
        isExpand.value = value === undefined ? !isExpand.value : value;
    }
    // 滚动到底部
    async function scrollToBottom() {
        await nextTick();
        document.querySelector(".cs-message .el-scrollbar__wrap")?.scroll({
            top: 9999,
            behavior: "smooth"
        });
    }
    return {
        isExpand,
        expand,
        scrollToBottom
    };
});
src/modules/cs/index.ts
New file
@@ -0,0 +1,13 @@
import { useMessage, useSession, useSocket } from "./hooks";
export function useCs() {
    const socket = useSocket();
    const message = useMessage();
    const session = useSession();
    return {
        socket,
        message,
        session
    };
}
src/modules/cs/static/audio/ding.wav
Binary files differ
src/modules/cs/static/emoji/angry-face.png
src/modules/cs/static/emoji/anguished-face.png
src/modules/cs/static/emoji/astonished-face.png
src/modules/cs/static/emoji/confounded-face.png
src/modules/cs/static/emoji/confused-face.png
src/modules/cs/static/emoji/crying-face.png
src/modules/cs/static/emoji/disappointed-but-relieved-face.png
src/modules/cs/static/emoji/disappointed-face.png
src/modules/cs/static/emoji/dizzy-face.png
src/modules/cs/static/emoji/drooling-face.png
src/modules/cs/static/emoji/expressionless-face.png
src/modules/cs/static/emoji/face-savouring-delicious-food.png
src/modules/cs/static/emoji/face-screaming-in-fear.png
src/modules/cs/static/emoji/face-throwing-a-kiss.png
src/modules/cs/static/emoji/face-with-cold-sweat.png
src/modules/cs/static/emoji/face-with-cowboy-hat.png
src/modules/cs/static/emoji/face-with-finger-covering-closed-lips.png
src/modules/cs/static/emoji/face-with-head-bandage.png
src/modules/cs/static/emoji/face-with-look-of-triumph.png
src/modules/cs/static/emoji/face-with-medical-mask.png
src/modules/cs/static/emoji/face-with-monocle.png
src/modules/cs/static/emoji/face-with-one-eyebrow-raised.png
src/modules/cs/static/emoji/face-with-open-mouth-and-cold-sweat.png
src/modules/cs/static/emoji/face-with-open-mouth-vomiting.png
src/modules/cs/static/emoji/face-with-open-mouth.png
src/modules/cs/static/emoji/face-with-party-horn-and-party-hat.png
src/modules/cs/static/emoji/face-with-pleading-eyes.png
src/modules/cs/static/emoji/face-with-rolling-eyes.png
src/modules/cs/static/emoji/face-with-stuck-out-tongue-and-tightly-closed-eyes.png
src/modules/cs/static/emoji/face-with-stuck-out-tongue-and-winking-eye.png
src/modules/cs/static/emoji/face-with-stuck-out-tongue.png
src/modules/cs/static/emoji/face-with-thermometer.png
src/modules/cs/static/emoji/face-with-uneven-eyes-and-wavy-mouth.png
src/modules/cs/static/emoji/face-without-mouth.png
src/modules/cs/static/emoji/fearful-face.png
src/modules/cs/static/emoji/flushed-face.png
src/modules/cs/static/emoji/freezing-face.png
src/modules/cs/static/emoji/frowning-face-with-open-mouth.png
src/modules/cs/static/emoji/grimacing-face.png
src/modules/cs/static/emoji/grinning-face-with-one-large-and-one-small-eye.png
src/modules/cs/static/emoji/grinning-face-with-smiling-eyes.png
src/modules/cs/static/emoji/grinning-face-with-star-eyes.png
src/modules/cs/static/emoji/grinning-face.png
src/modules/cs/static/emoji/hugging-face.png
src/modules/cs/static/emoji/hushed-face.png
src/modules/cs/static/emoji/imp.png
src/modules/cs/static/emoji/kissing-face-with-closed-eyes.png
src/modules/cs/static/emoji/kissing-face-with-smiling-eyes.png
src/modules/cs/static/emoji/kissing-face.png
src/modules/cs/static/emoji/loudly-crying-face.png
src/modules/cs/static/emoji/lying-face.png
src/modules/cs/static/emoji/money-mouth-face.png
src/modules/cs/static/emoji/nauseated-face.png
src/modules/cs/static/emoji/nerd-face.png
src/modules/cs/static/emoji/neutral-face.png
src/modules/cs/static/emoji/overheated-face.png
src/modules/cs/static/emoji/pensive-face.png
src/modules/cs/static/emoji/persevering-face.png
src/modules/cs/static/emoji/pouting-face.png
src/modules/cs/static/emoji/relieved-face.png
src/modules/cs/static/emoji/rolling-on-the-floor-laughing.png
src/modules/cs/static/emoji/serious-face-with-symbols-covering-mouth.png
src/modules/cs/static/emoji/shocked-face-with-exploding-head.png
src/modules/cs/static/emoji/sleeping-face.png
src/modules/cs/static/emoji/sleepy-face.png
src/modules/cs/static/emoji/slightly-frowning-face.png
src/modules/cs/static/emoji/slightly-smiling-face.png
src/modules/cs/static/emoji/smiling-face-with-halo.png
src/modules/cs/static/emoji/smiling-face-with-heart-shaped-eyes.png
src/modules/cs/static/emoji/smiling-face-with-horns.png
src/modules/cs/static/emoji/smiling-face-with-open-mouth-and-smiling-eyes.png
src/modules/cs/static/emoji/smiling-face-with-open-mouth-and-tightly-closed-eyes.png
src/modules/cs/static/emoji/smiling-face-with-open-mouth.png
src/modules/cs/static/emoji/smiling-face-with-smiling-eyes-and-hand-covering-mouth.png
src/modules/cs/static/emoji/smiling-face-with-smiling-eyes-and-three-hearts.png
src/modules/cs/static/emoji/smiling-face-with-smiling-eyes.png
src/modules/cs/static/emoji/smiling-face-with-sunglasses.png
src/modules/cs/static/emoji/smirking-face.png
src/modules/cs/static/emoji/sneezing-face.png
src/modules/cs/static/emoji/thinking-face.png
src/modules/cs/static/emoji/tired-face.png
src/modules/cs/static/emoji/upside-down-face.png
src/modules/cs/static/emoji/weary-face.png
src/modules/cs/static/emoji/white-frowning-face.png
src/modules/cs/static/emoji/white-smiling-face.png
src/modules/cs/static/emoji/winking-face.png
src/modules/cs/static/emoji/worried-face.png
src/modules/cs/static/emoji/zipper-mouth-face.png
src/modules/cs/types/index.d.ts
New file
@@ -0,0 +1,13 @@
export declare namespace Cs {
    interface Content {
        type: "text" | "image" | "voice" | "video" | "file" | "link" | "location" | "emoji";
        data: string;
    }
    interface Msg extends Eps.CsMsgEntity {
        content: Content;
        sessionId?: number;
    }
    interface Session extends Eps.CsSessionEntity {}
}
src/modules/cs/utils/index.ts
New file
@@ -0,0 +1,52 @@
import dayjs from "dayjs";
import type { Cs } from "../types";
// 消息格式化
export function msgFormatter(msg: Cs.Msg) {
    if (!msg) {
        return "";
    }
    switch (msg.content.type) {
        case "text":
            return msg.content.data;
        case "emoji":
            return "【表情】";
        case "image":
            return "【图片】";
        case "link":
            return "【链接】";
        case "voice":
            return "【音频】";
        case "file":
            return "【文件】";
        case "video":
            return "【视频】";
        case "location":
            return "【定位】";
    }
}
// 日期格式化
export function dateFormatter(date?: Date) {
    const t = dayjs(date);
    // 在今天之前
    if (t.isBefore(dayjs().hour(0).minute(0).second(0))) {
        return t.format("YYYY-MM-DD HH:mm");
    } else {
        return t.format("HH:mm");
    }
}
// 表情链接
export function getEmoji(name: string) {
    return new URL(`../static/emoji/${name}`, import.meta.url).href;
}
src/modules/demo/views/home/components/count-category.vue
New file
@@ -0,0 +1,128 @@
<template>
    <div class="category-ratio">
        <div class="card">
            <div class="card__header">
                <span class="label">销售类别占比</span>
                <el-icon class="more" @click="router.push('/goods/category')">
                    <arrow-right-bold />
                </el-icon>
            </div>
            <div class="card__container">
                <v-chart :option="chartOption" autoresize />
            </div>
        </div>
    </div>
</template>
<script lang="ts" setup>
import { onMounted, reactive, ref } from "vue";
import { useCool } from "/@/cool";
import { ArrowRightBold } from "@element-plus/icons-vue";
import { orderBy } from "lodash-es";
const { service, router } = useCool();
const list = ref<{ typeName: string; total: number }[]>([]);
const chartOption = reactive({
    legend: {
        top: 50,
        left: 15,
        orient: "vertical",
        icon: "circle",
        data: [] as string[],
        formatter(name: string) {
            const d = list.value.find((e) => e.typeName === name);
            return `${d?.typeName}(${d?.total})`;
        }
    },
    series: [
        {
            type: "pie",
            padAngle: 5,
            radius: ["45%", "60%"],
            center: ["75%", "55%"],
            avoidLabelOverlap: false,
            label: {
                show: false,
                position: "center"
            },
            emphasis: {
                label: {
                    show: true,
                    fontSize: "15",
                    fontWeight: "bold"
                }
            },
            labelLine: {
                show: false
            },
            data: [] as any[],
            itemStyle: {
                borderColor: "#fff",
                borderWidth: 2,
                borderRadius: 4
            }
        }
    ]
});
onMounted(() => {
    service.count.home
        .goodsCategory({
            type: "count"
        })
        .then((res: any[]) => {
            // 倒序
            const arr = orderBy(res, "total", "desc").map((e) => {
                return {
                    ...e,
                    total: Number(e.total)
                };
            });
            // 前5个先
            const rank = arr.filter((_, i) => i < 5);
            // 合并其他
            const other = {
                typeName: "其他",
                total: arr.splice(5).reduce((a, b) => a + b.total, 0)
            };
            list.value = [...rank, other];
            // 设置图例
            chartOption.legend.data = list.value.map((e) => e.typeName);
            // 设置数据
            chartOption.series[0].data = list.value.map((e) => {
                return {
                    value: e.total,
                    name: e.typeName
                };
            });
        });
});
</script>
<style lang="scss" scoped>
.category-ratio {
    position: relative;
    .echarts {
        height: 150px;
        position: absolute;
        right: 0;
        top: 0;
    }
    .card__container {
        height: 100px;
    }
}
</style>
src/modules/demo/views/home/components/goods-rank.vue
New file
@@ -0,0 +1,93 @@
<template>
    <div class="goods-rank">
        <div class="goods-rank__header">
            <span>商品销售排行</span>
        </div>
        <div class="goods-rank__container">
            <div class="goods-rank__table">
                <cl-crud ref="Crud" padding="0">
                    <cl-table ref="Table" :border="false" />
                </cl-crud>
            </div>
        </div>
    </div>
</template>
<script lang="ts" setup>
import { useCrud, useTable } from "@cool-vue/crud";
import { useCool } from "/@/cool";
const { service } = useCool();
const Crud = useCrud(
    {
        service: service.count.home,
        dict: {
            api: {
                page: "goodsRank"
            }
        }
    },
    (app) => {
        app.refresh({
            type: "amount",
            limit: 10
        });
    }
);
const Table = useTable({
    autoHeight: false,
    contextMenu: [],
    columns: [
        {
            label: "排名",
            type: "index",
            width: 60
        },
        {
            label: "商品标题",
            prop: "title",
            align: "left",
            showOverflowTooltip: true,
            minWidth: 200
        },
        {
            label: "商品图标",
            prop: "mainPic",
            width: 140,
            component: {
                name: "cl-image",
                props: {
                    size: 40
                }
            }
        },
        {
            label: "销售金额",
            prop: "total",
            width: 140
        }
    ]
});
</script>
<style lang="scss" scoped>
.goods-rank {
    &__header {
        display: flex;
        align-items: center;
        height: 50px;
        font-size: 15px;
        font-weight: bold;
        padding: 0 20px;
    }
    &__table {
        padding: 0 10px 20px 10px;
        margin: 0 10px;
        background-color: var(--el-bg-color);
    }
}
</style>
src/modules/demo/views/test/crud.vue
New file
@@ -0,0 +1,205 @@
<template>
    <cl-crud ref="Crud">
        <cl-row>
            <cl-refresh-btn />
            <cl-add-btn />
            <cl-flex1 />
            <cl-search ref="Search" />
        </cl-row>
        <cl-row>
            <cl-table ref="Table" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <cl-pagination />
        </cl-row>
        <cl-upsert ref="Upsert" />
    </cl-crud>
</template>
<script lang="ts" setup>
defineOptions({
    name: 'user-info'
});
import { useCrud, useTable, useUpsert, useSearch } from '@cool-vue/crud';
import { useCool } from '/@/cool';
import { useDict } from '/$/dict';
import { useI18n } from 'vue-i18n';
import { reactive } from 'vue';
const { service } = useCool();
const { t } = useI18n();
const { dict } = useDict();
const options = reactive({
    gender: [
        { label: t('未知'), value: 0, type: 'default' },
        { label: t('男'), value: 1, type: 'success' },
        { label: t('女'), value: 2, type: 'danger' }
    ],
    status: [
        { label: t('禁用'), value: 0, type: 'danger' },
        { label: t('正常'), value: 1, type: 'success' },
        { label: t('已注销'), value: 2, type: 'warning' }
    ],
    loginType: [
        { label: t('小程序'), value: 0, type: 'default' },
        { label: t('公众号'), value: 1, type: 'success' },
        { label: t('H5'), value: 2, type: 'info' }
    ]
});
const Upsert = useUpsert({
    items: [
        {
            label: t('登录唯一ID'),
            prop: 'unionid',
            component: { name: 'el-input' },
            required: false,
            span: 12
        },
        {
            label: t('头像'),
            prop: 'avatarUrl',
            component: { name: 'cl-upload', props: { size: 120 } },
            required: false,
            span: 12
        },
        {
            label: t('昵称'),
            prop: 'nickName',
            component: { name: 'el-input' },
            required: true,
            span: 12
        },
        {
            label: t('手机号'),
            prop: 'phone',
            component: { name: 'el-input' },
            required: false,
            span: 12
        },
        {
            label: t('性别'),
            prop: 'gender',
            component: { name: 'cl-select', props: { options: options.gender } },
            required: true,
            value: 0,
            span: 12
        },
        {
            label: t('状态'),
            prop: 'status',
            component: { name: 'cl-select', props: { options: options.status } },
            required: true,
            value: 1,
            span: 12
        },
        {
            label: t('登录方式'),
            prop: 'loginType',
            component: { name: 'cl-select', props: { options: options.loginType } },
            required: true,
            value: 0,
            span: 12
        },
        {
            label: t('来源'),
            prop: 'source',
            component: { name: 'cl-select', props: { dict: dict.get('sourceType') } },
            required: true,
            value: 0,
            span: 12
        },
        {
            label: t('密码'),
            prop: 'password',
            component: { name: 'el-input' },
            required: false,
            span: 12
        },
        {
            label: t('介绍'),
            prop: 'description',
            component: { name: 'el-input', props: { type: 'textarea' } },
            required: false,
            span: 24
        },
        {
            label: t('余额'),
            prop: 'balance',
            component: { name: 'el-input-number', props: { precision: 2 } },
            required: true,
            value: 0,
            span: 12
        },
        {
            label: t('标题'),
            prop: 'goodsName',
            component: { name: 'el-input' },
            required: true,
            span: 12
        }
    ]
});
const Table = useTable({
    columns: [
        { label: t('ID'), prop: 'id', minWidth: 120 },
        { label: t('登录唯一ID'), prop: 'unionid', minWidth: 120, showOverflowTooltip: true },
        {
            label: t('头像'),
            prop: 'avatarUrl',
            minWidth: 120,
            component: { name: 'cl-image', props: { size: ['50px', '50px'] } }
        },
        { label: t('昵称'), prop: 'nickName', minWidth: 120, showOverflowTooltip: true },
        { label: t('手机号'), prop: 'phone', minWidth: 120, showOverflowTooltip: true },
        { label: t('性别'), prop: 'gender', minWidth: 120, dict: options.gender },
        { label: t('状态'), prop: 'status', minWidth: 120, dict: options.status },
        { label: t('登录方式'), prop: 'loginType', minWidth: 120, dict: options.loginType },
        { label: t('来源'), prop: 'source', minWidth: 120, dict: dict.get('sourceType') },
        { label: t('标题'), prop: 'goodsName', minWidth: 120, showOverflowTooltip: true },
        { label: t('创建时间'), prop: 'createTime', minWidth: 170, sortable: 'desc' },
        { label: t('更新时间'), prop: 'updateTime', minWidth: 170, sortable: 'custom' }
    ]
});
const Crud = useCrud(
    {
        service: service.user.info
    },
    app => {
        app.refresh();
    }
);
const Search = useSearch({
    items: [
        {
            label: t('性别'),
            prop: 'gender',
            component: { name: 'cl-select', props: { options: options.gender } }
        },
        {
            label: t('状态'),
            prop: 'status',
            component: { name: 'cl-select', props: { options: options.status } }
        },
        {
            prop: 'keyWord',
            component: {
                name: 'el-input',
                props: {
                    placeholder: t('搜索手机号、昵称'),
                    clearable: true
                }
            }
        }
    ]
});
</script>
src/modules/goods/components/select.vue
New file
@@ -0,0 +1,371 @@
<template>
    <div class="goods-select__inner">
        <template v-if="multiple">
            <div class="btns">
                <el-button type="success" @click="open">添加</el-button>
                <el-button
                    type="danger"
                    :disabled="refs.table?.selection.length == 0"
                    @click="remove"
                    >移除</el-button
                >
            </div>
            <cl-crud padding="0">
                <cl-table :data="data" :ref="setRefs('table')" :auto-height="false" />
                <cl-row type="flex" align="middle" justify="end" :style="{ marginTop: '10px' }">
                    <el-pagination
                        v-model:current-page="pager.page"
                        :page-size="pager.size"
                        :total="list.length"
                        background
                        layout="total, prev, pager, next, jumper"
                    />
                </cl-row>
            </cl-crud>
        </template>
        <template v-else>
            <slot>
                <div class="goods" @click="open">
                    <template v-if="data[0]">
                        <cl-avatar :size="24" :src="data[0].mainPic"></cl-avatar>
                        <span>{{ data[0].name }}</span>
                        <el-icon @click.stop="remove">
                            <circle-close />
                        </el-icon>
                    </template>
                    <span class="placeholder" v-else>请选择商品</span>
                </div>
            </slot>
        </template>
    </div>
    <cl-dialog v-model="visible" width="1200px" title="选择商品">
        <cl-crud ref="Crud" padding="0">
            <cl-row>
                <!-- 刷新按钮 -->
                <cl-refresh-btn />
                <!-- 全选 -->
                <el-button type="primary" @click="selectAll" v-if="multiple">全选</el-button>
                <cl-filter label="分类">
                    <cl-select
                        tree
                        :options="options.type"
                        prop="typeId"
                        all-levels-id
                        :width="140"
                    />
                </cl-filter>
                <cl-filter label="状态">
                    <cl-select :options="options.status" prop="status" :width="140" />
                </cl-filter>
                <cl-flex1 />
                <!-- 关键字搜索 -->
                <cl-search-key placeholder="搜索标题" />
            </cl-row>
            <cl-row>
                <!-- 数据表格 -->
                <cl-table ref="Table" :auto-height="false" @selection-change="onSelectionChange">
                    <template #column-check="{ scope }">
                        <el-button type="success" disabled v-if="selection[0]?.id == scope.row.id"
                            >已选</el-button
                        >
                        <el-button @click="select(scope.row)" v-else>选择</el-button>
                    </template>
                </cl-table>
            </cl-row>
            <cl-row>
                <span v-if="multiple">已选 {{ selection.length }} 件</span>
                <cl-flex1 />
                <!-- 分页控件 -->
                <cl-pagination />
            </cl-row>
        </cl-crud>
        <template #footer>
            <el-button @click="close">取消</el-button>
            <el-button
                type="success"
                :disabled="isEmpty(selection)"
                @click="select()"
                v-if="multiple"
                >选择</el-button
            >
        </template>
    </cl-dialog>
</template>
<script lang="ts" setup name="goods-select">
import { useCrud, useForm, useTable } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import { type PropType, computed, nextTick, reactive, ref, watch } from "vue";
import { cloneDeep, isEmpty } from "lodash-es";
import { CircleClose } from "@element-plus/icons-vue";
import type { Dict } from "/$/dict/types";
import { deepTree } from "/@/cool/utils";
// 替换你的类型
type Item = Eps.GoodsInfoEntity;
const props = defineProps({
    modelValue: {
        type: Array as PropType<Item[]>,
        default: () => []
    },
    isDisabled: Boolean,
    prop: String,
    scope: null,
    disabled: Boolean,
    // 是否多选
    multiple: {
        type: Boolean,
        default: true
    }
});
const emit = defineEmits(["update:modelValue", "change"]);
const { service, refs, setRefs } = useCool();
// 上级表单
const Form = useForm();
// 选项
const options = reactive({
    status: [
        {
            label: "启用",
            value: 1,
            type: "success"
        },
        {
            label: "禁用",
            value: 0,
            type: "danger"
        }
    ],
    type: [] as Dict.Item[]
});
// cl-table
const Table = useTable({
    contextMenu: [],
    columns: [
        props.multiple
            ? {
                    type: "selection",
                    width: 60,
                    reserveSelection: true
              }
            : {
                    label: "操作",
                    prop: "check",
                    width: 100
              },
        { label: "分类", prop: "typeId", minWidth: 160, dict: computed(() => options.type) },
        { label: "标题", prop: "title", minWidth: 240 },
        {
            label: "主图",
            prop: "mainPic",
            minWidth: 100,
            component: { name: "cl-image", props: { size: 50 } }
        },
        {
            label: "示例图",
            prop: "pics",
            minWidth: 100,
            component: { name: "cl-image", props: { size: 50 } }
        },
        { label: "价格", prop: "price", minWidth: 100, sortable: "custom" },
        { label: "已售", prop: "sold", minWidth: 100, sortable: "custom" },
        { label: "排序", prop: "sortNum", minWidth: 100, sortable: "desc" },
        {
            label: "状态",
            prop: "status",
            minWidth: 100,
            dict: options.status
        },
        {
            label: "创建时间",
            prop: "createTime",
            sortable: "desc",
            minWidth: 160
        }
    ]
});
// cl-crud
const Crud = useCrud({
    service: service.goods.info,
    async onRefresh(params, { next }) {
        const res = await next(params);
        // 添加已加载列表的 id
        loadIds.value.push(...res.list.map((e) => e.id));
        // 数据反选
        selection.value.forEach((e) => {
            const d = Table.value?.data.find((a) => a.id == e.id);
            if (d) {
                Table.value?.toggleRowSelection(d, true);
            }
        });
    }
});
// 刷新
async function refresh(params?: any) {
    return Crud.value?.refresh(params);
}
// 弹窗是否可见
const visible = ref(false);
// 已选的数据列表,双向绑定用
const list = ref<Item[]>([]);
// 已选列表
const selection = ref<any[]>([]);
// 分页
const pager = reactive({
    page: 1,
    size: 10
});
// 分页数据
const data = computed(() => {
    const { page, size } = pager;
    return list.value.slice((page - 1) * size, page * size);
});
// 已加载列表的 id
const loadIds = ref<number[]>([]);
// 监听已选列表
function onSelectionChange(arr: Item[]) {
    // 已加载的
    const ids = Array.from(new Set(loadIds.value));
    // 过滤掉已加载的,再加上已选的
    selection.value = selection.value.filter((e) => !ids.includes(e.id!)).concat(...arr);
}
// 打开选择弹窗
function open() {
    visible.value = true;
    // 清空数据
    loadIds.value = [];
    // 设置已选
    selection.value = cloneDeep(list.value);
    nextTick(() => {
        // 获取分类
        service.goods.type.list().then((res) => {
            res.forEach((e) => {
                e.label = e.name;
                e.value = e.id;
            });
            options.type = deepTree(res);
        });
        refresh({
            size: 10
        });
    });
}
// 关闭选择弹窗
function close() {
    visible.value = false;
}
// 选择
function select(item?: Item) {
    // 单选不触发 onSelectionChange 手动设置
    if (item) {
        selection.value = [item];
    }
    list.value = cloneDeep(selection.value || []);
    close();
}
// 全选
async function selectAll() {
    // 全部数据
    await Crud.value?.refresh({ page: 1, size: 10000 }).then((res) => {
        list.value = res.list;
    });
    // 当前页数据
    // list.value = Table.value?.data || [];
    close();
}
// 移除
function remove() {
    const ids = selection.value.map((e) => e.id);
    list.value = list.value.filter((e) => {
        // 清空选择状态
        refs.table?.toggleRowSelection(e, false);
        // 移除已选的
        return !ids.find((id) => id == e.id);
    });
}
// 监听已选列表,返回 ids/id
watch(
    list,
    (arr = []) => {
        const ids = arr.map((e) => e.id);
        if (props.multiple) {
            emit("update:modelValue", ids);
            emit("change", ids);
        } else {
            emit("update:modelValue", ids[0]);
            emit("change", ids[0]);
        }
        Form.value?.validateField(props.prop);
    },
    {
        deep: true
    }
);
defineExpose({
    remove,
    select,
    selectAll,
    open
});
</script>
<style lang="scss" scoped>
.goods-select__inner {
    .btns {
        margin-bottom: 10px;
    }
}
</style>
src/modules/goods/components/spec.vue
New file
@@ -0,0 +1,322 @@
<template>
    <cl-form ref="Form">
        <template #slot-spec>
            <cl-crud padding="0">
                <cl-row>
                    <el-button type="success" @click="add">添加规格</el-button>
                    <el-button
                        :disabled="Table?.selection.length == 0"
                        type="danger"
                        @click="remove()"
                        >删除</el-button
                    >
                </cl-row>
                <cl-row>
                    <cl-table ref="Table" :data="data" :auto-height="false">
                        <template #column-images="{ scope }">
                            <cl-upload
                                small
                                :size="40"
                                draggable
                                multiple
                                v-model="scope.row.images"
                                @change="onChange(scope.row)"
                            />
                        </template>
                        <template #column-name="{ scope }">
                            <el-form-item
                                :prop="`spec.${scope.$index}.name`"
                                :rules="{
                                    required: true,
                                    message: '名称不能为空'
                                }"
                            >
                                <el-input
                                    v-model="scope.row.name"
                                    clearable
                                    placeholder="请输入名称"
                                    @change="onChange(scope.row)"
                                />
                            </el-form-item>
                        </template>
                        <template #column-price="{ scope }">
                            <el-form-item
                                :prop="`spec.${scope.$index}.price`"
                                :rules="{
                                    required: true,
                                    message: '价格不能为空'
                                }"
                            >
                                <el-input-number
                                    v-model="scope.row.price"
                                    :min="0.01"
                                    :max="100000"
                                    :precision="2"
                                    :controls="false"
                                    placeholder="价格"
                                    @change="onChange(scope.row)"
                                />
                            </el-form-item>
                        </template>
                        <template #column-stock="{ scope }">
                            <el-form-item>
                                <el-input-number
                                    v-model="scope.row.stock"
                                    :min="0"
                                    :max="100000"
                                    :precision="0"
                                    :controls="false"
                                    placeholder="库存"
                                    @change="onChange(scope.row)"
                                />
                            </el-form-item>
                        </template>
                        <template #column-sortNum="{ scope }">
                            <el-form-item>
                                <el-input-number
                                    v-model="scope.row.sortNum"
                                    :min="0"
                                    :max="100000"
                                    :precision="0"
                                    :controls="false"
                                    placeholder="排序"
                                />
                            </el-form-item>
                        </template>
                        <template #slot-btn="{ scope }">
                            <el-icon class="del-btn" @click="remove(scope.row.uid)">
                                <delete />
                            </el-icon>
                        </template>
                    </cl-table>
                </cl-row>
            </cl-crud>
        </template>
    </cl-form>
</template>
<script lang="ts" setup name="goods-spec">
import { useForm, useTable } from "@cool-vue/crud";
import { computed, ref } from "vue";
import { useCool } from "/@/cool";
import { ElMessage } from "element-plus";
import { isEmpty, orderBy } from "lodash-es";
import { Delete } from "@element-plus/icons-vue";
import { uuid } from "/@/cool/utils";
const { service } = useCool();
const Form = useForm();
// 规格列表
const list = ref<Eps.GoodsSpecEntity[]>([]);
// 表格数据
const data = computed(() => {
    return list.value.filter((e) => !e.isDel);
});
// 以详情方式打开
async function openById(id: number) {
    const data = await service.goods.info.info({ id });
    if (data) {
        open(data);
    }
}
// 打开
async function open(info: Eps.GoodsInfoEntity) {
    await refresh(info.id!);
    Form.value?.open({
        title: "编辑规格",
        form: {
            spec: list.value
        },
        width: "70%",
        items: [
            {
                prop: "spec",
                component: {
                    name: "slot-spec"
                }
            }
        ],
        on: {
            submit(_, { close, done }) {
                const delIds: number[] = [];
                const add: Eps.GoodsSpecEntity[] = [];
                const update: Eps.GoodsSpecEntity[] = [];
                list.value.forEach((e) => {
                    if (e.id) {
                        if (e.isDel) {
                            delIds.push(e.id);
                        }
                        if (e.isEdit) {
                            update.push(e);
                        }
                    } else {
                        if (!e.isDel) {
                            add.push(e);
                        }
                    }
                });
                const arr: Promise<any>[] = [];
                // 要删除的
                if (!isEmpty(delIds)) {
                    arr.push(service.goods.spec.delete({ ids: delIds }));
                }
                // 要更新的
                if (!isEmpty(update)) {
                    arr.push(
                        ...update.map((e) => {
                            return service.goods.spec.update(e);
                        })
                    );
                }
                // 要添加的
                if (!isEmpty(add)) {
                    arr.push(
                        service.goods.spec.add(
                            add.map((e) => {
                                return {
                                    ...e,
                                    goodsId: info.id
                                };
                            })
                        )
                    );
                }
                Promise.all(arr)
                    .then(() => {
                        ElMessage.success("保存成功");
                        close();
                    })
                    .catch((err) => {
                        ElMessage.error(err.message);
                        refresh(info.id!);
                        done();
                    });
            }
        }
    });
}
// 刷新
async function refresh(goodsId: number) {
    await service.goods.spec.list({ goodsId, order: "sortNum", sort: "desc" }).then((res) => {
        list.value = res.map((e) => {
            return {
                ...e,
                price: Number(e.price),
                uid: e.id
            };
        });
    });
}
// 添加
function add() {
    list.value.push({
        uid: uuid(),
        name: "",
        images: []
    });
}
// 移除
function remove(uid?: string) {
    const arr = uid ? [uid] : Table.value?.selection.map((e) => e.uid);
    list.value.forEach((e) => {
        if (arr?.includes(e.uid)) {
            e.isDel = true;
        }
    });
}
// 输入改变
function onChange(item: Eps.GoodsSpecEntity, prop?: string) {
    item.isEdit = true;
    // 排序改变
    if (prop == "sortNum") {
        list.value = orderBy(list.value, "sortNum", "desc");
    }
}
const Table = useTable({
    columns: [
        {
            type: "selection"
        },
        {
            label: "名称",
            prop: "name",
            minWidth: 200,
            align: "left"
        },
        {
            label: "图片",
            prop: "images",
            minWidth: 160,
            align: "left"
        },
        {
            label: "价格",
            prop: "price",
            minWidth: 140,
            align: "left"
        },
        {
            label: "库存",
            prop: "stock",
            minWidth: 120,
            align: "left"
        },
        {
            label: "排序",
            prop: "sortNum",
            minWidth: 120,
            align: "left"
        },
        {
            type: "op",
            width: 80,
            buttons: ["slot-btn"]
        }
    ]
});
defineExpose({
    open,
    openById
});
</script>
<style lang="scss" scoped>
.el-form-item {
    margin-bottom: 0;
}
.del-btn {
    cursor: pointer;
    &:hover {
        color: red;
    }
}
</style>
src/modules/goods/views/comment.vue
New file
@@ -0,0 +1,72 @@
<template>
    <cl-crud ref="Crud">
        <cl-view-head />
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <cl-flex1 />
            <!-- 关键字搜索 -->
            <cl-search-key placeholder="搜索用户昵称、评论内容" />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
    </cl-crud>
</template>
<script lang="ts" name="goods-comment" setup>
import { useCrud, useTable } from "@cool-vue/crud";
import { useCool } from "/@/cool";
const { service, route } = useCool();
// cl-table
const Table = useTable({
    contextMenu: [],
    columns: [
        { label: "#", type: "index" },
        { label: "用户昵称", prop: "nickName", minWidth: 140 },
        { label: "内容", prop: "content", showOverflowTooltip: true, minWidth: 200 },
        {
            label: "星数",
            prop: "starCount",
            minWidth: 150,
            component: { name: "el-rate", props: { disabled: true } }
        },
        {
            label: "图片",
            prop: "pics",
            minWidth: 100,
            component: { name: "cl-image", props: { size: 60 } }
        },
        {
            label: "创建时间",
            prop: "createTime",
            minWidth: 160,
            component: { name: "cl-date-text" }
        }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        service: service.goods.comment
    },
    (app) => {
        app.refresh({
            goodsId: route.query.id
        });
    }
);
</script>
src/modules/goods/views/info.vue
New file
@@ -0,0 +1,277 @@
<template>
    <cl-crud ref="Crud">
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <!-- 新增按钮 -->
            <cl-add-btn />
            <!-- 删除按钮 -->
            <cl-multi-delete-btn />
            <cl-filter label="分类">
                <cl-select tree :options="options.type" prop="typeId" all-levels-id :width="140" />
            </cl-filter>
            <cl-filter label="状态">
                <cl-select :options="options.status" prop="status" :width="140" />
            </cl-filter>
            <cl-flex1 />
            <!-- 关键字搜索 -->
            <cl-search-key placeholder="搜索标题" />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
        <!-- 新增、编辑 -->
        <cl-upsert ref="Upsert" />
        <!-- 商品规格 -->
        <goods-spec :ref="setRefs('goodsSpec')" />
    </cl-crud>
</template>
<script lang="ts" name="goods-info" setup>
import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
import { router, useCool } from '/@/cool';
import { computed, onActivated, reactive } from 'vue';
import { deepTree } from '/@/cool/utils';
import type { Dict } from '/$/dict/types';
import GoodsSpec from '../components/spec.vue';
const { service, refs, setRefs } = useCool();
const options = reactive({
    type: [] as Dict.Item[],
    status: [
        {
            label: '已上架',
            value: 1
        },
        {
            label: '已下架',
            value: 0
        }
    ]
});
// cl-upsert
const Upsert = useUpsert({
    dialog: {
        width: '1000px'
    },
    items: [
        {
            type: 'tabs',
            props: {
                labels: [
                    {
                        label: '商品信息',
                        value: 'base'
                    },
                    {
                        label: '商品介绍',
                        value: 'detail'
                    }
                ]
            }
        },
        {
            group: 'base',
            label: '分类',
            prop: 'typeId',
            component: {
                name: 'cl-select',
                props: {
                    options: computed(() => options.type),
                    tree: true
                }
            },
            required: true
        },
        {
            group: 'base',
            label: '标题',
            prop: 'title',
            component: { name: 'el-input', props: { clearable: true } },
            required: true
        },
        {
            group: 'base',
            label: '副标题',
            prop: 'subTitle',
            component: { name: 'el-input', props: { clearable: true, type: 'textarea', rows: 3 } }
        },
        {
            group: 'base',
            label: '主图',
            prop: 'mainPic',
            component: { name: 'cl-upload' },
            required: true
        },
        {
            group: 'base',
            label: '示例图',
            prop: 'pics',
            component: { name: 'cl-upload', props: { multiple: true, draggable: true } }
        },
        {
            group: 'base',
            label: '价格',
            prop: 'price',
            hook: 'number',
            span: 12,
            component: {
                name: 'el-input-number',
                props: {
                    min: 0.01,
                    max: 1000000
                }
            },
            required: true
        },
        {
            group: 'base',
            label: '已售',
            prop: 'sold',
            hook: 'number',
            span: 12,
            component: {
                name: 'el-input-number',
                props: {
                    min: 0,
                    max: 1000000
                }
            },
            required: true
        },
        {
            group: 'base',
            label: '排序',
            prop: 'sortNum',
            hook: 'number',
            component: { name: 'el-input-number' }
        },
        {
            group: 'base',
            label: '状态',
            prop: 'status',
            flex: false,
            value: 1,
            component: { name: 'cl-switch' },
            required: true
        },
        {
            group: 'detail',
            prop: 'content',
            component: {
                name: 'cl-editor-wang'
            }
        }
    ],
    async onSubmit(data, { next }) {
        const info = await next(data);
        // 新增后弹出规格添加
        if (Upsert.value?.mode == 'add') {
            refs.goodsSpec.openById(info.id);
        }
    }
});
// cl-table
const Table = useTable({
    columns: [
        { type: 'selection' },
        { label: '分类', prop: 'typeId', minWidth: 160, dict: computed(() => options.type) },
        { label: '标题', prop: 'title', minWidth: 240 },
        {
            label: '主图',
            prop: 'mainPic',
            minWidth: 100,
            component: { name: 'cl-image', props: { size: 50 } }
        },
        {
            label: '示例图',
            prop: 'pics',
            minWidth: 100,
            component: { name: 'cl-image', props: { size: 50 } }
        },
        { label: '价格', prop: 'price', minWidth: 100, sortable: 'custom' },
        { label: '已售', prop: 'sold', minWidth: 100, sortable: 'custom' },
        { label: '状态', prop: 'status', minWidth: 100, component: { name: 'cl-switch' } },
        { label: '排序', prop: 'sortNum', minWidth: 100, sortable: 'desc' },
        { label: '创建时间', prop: 'createTime', minWidth: 160, sortable: 'custom' },
        {
            label: '更新时间',
            prop: 'updateTime',
            minWidth: 160,
            sortable: 'custom'
        },
        {
            type: 'op',
            width: 300,
            buttons({ scope }) {
                return [
                    'edit',
                    'delete',
                    {
                        label: '规格',
                        onClick() {
                            refs.goodsSpec.open(scope.row);
                        }
                    },
                    {
                        label: '评论',
                        onClick() {
                            router.push({
                                path: '/goods/comment',
                                query: {
                                    id: scope.row.id,
                                    title: scope.row.title
                                }
                            });
                        }
                    }
                ];
            }
        }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        service: service.goods.info
    },
    app => {
        app.refresh();
    }
);
// 获取分类
function getTypes() {
    service.goods.type.list().then(res => {
        res.forEach(e => {
            e.label = e.name;
            e.value = e.id;
        });
        options.type = deepTree(res);
    });
}
onActivated(() => {
    getTypes();
});
</script>
src/modules/goods/views/search-keyword.vue
New file
@@ -0,0 +1,85 @@
<template>
    <cl-crud ref="Crud">
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <!-- 新增按钮 -->
            <cl-add-btn />
            <!-- 删除按钮 -->
            <cl-multi-delete-btn />
            <cl-flex1 />
            <!-- 关键字搜索 -->
            <cl-search-key placeholder="搜索名称" />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
        <!-- 新增、编辑 -->
        <cl-upsert ref="Upsert" />
    </cl-crud>
</template>
<script lang="ts" name="goods-search-keyword" setup>
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { useCool } from "/@/cool";
const { service } = useCool();
// cl-upsert
const Upsert = useUpsert({
    dialog: {
        width: "500px"
    },
    props: {
        labelWidth: "60px"
    },
    items: [
        {
            label: "名称",
            prop: "name",
            component: { name: "el-input", props: { clearable: true } },
            required: true
        },
        { label: "排序", prop: "sortNum", hook: "number", component: { name: "el-input-number" } }
    ]
});
// cl-table
const Table = useTable({
    columns: [
        { type: "selection" },
        { label: "名称", prop: "name", minWidth: 140 },
        { label: "排序", prop: "sortNum", minWidth: 140, sortable: "desc" },
        {
            label: "创建时间",
            prop: "createTime",
            minWidth: 160
        },
        {
            label: "更新时间",
            prop: "updateTime",
            minWidth: 160
        },
        { type: "op", buttons: ["edit", "delete"] }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        service: service.goods.searchKeyword
    },
    (app) => {
        app.refresh();
    }
);
</script>
src/modules/goods/views/type.vue
New file
@@ -0,0 +1,163 @@
<template>
    <cl-view-group ref="ViewGroup">
        <template #right>
            <cl-crud ref="Crud">
                <cl-row>
                    <!-- 刷新按钮 -->
                    <cl-refresh-btn />
                    <!-- 新增按钮 -->
                    <cl-add-btn />
                    <!-- 删除按钮 -->
                    <cl-multi-delete-btn />
                    <cl-flex1 />
                    <!-- 关键字搜索 -->
                    <cl-search-key placeholder="搜索名称" />
                </cl-row>
                <cl-row>
                    <!-- 数据表格 -->
                    <cl-table ref="Table" />
                </cl-row>
                <cl-row>
                    <cl-flex1 />
                    <!-- 分页控件 -->
                    <cl-pagination />
                </cl-row>
                <!-- 新增、编辑 -->
                <cl-upsert ref="Upsert" />
            </cl-crud>
        </template>
    </cl-view-group>
</template>
<script lang="tsx" name="goods-type" setup>
import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
import { useCool } from '/@/cool';
import { useViewGroup } from '/@/plugins/view';
import { deepTree } from '/@/cool/utils';
import { setFocus } from '/@/plugins/crud/plugins/form/setFocus';
const { service } = useCool();
const { ViewGroup } = useViewGroup<Eps.GoodsTypeEntity>({
    service: service.goods.type,
    label: '一级分类',
    title: '二级分类',
    data: {
        size: 1000
    },
    onEdit() {
        return {
            width: '500px',
            props: {
                labelWidth: '60px'
            },
            items: [
                {
                    label: '名称',
                    prop: 'name',
                    required: true,
                    component: {
                        name: 'el-input'
                    }
                },
                {
                    label: '图标',
                    prop: 'pic',
                    component: {
                        name: 'cl-upload'
                    }
                },
                {
                    label: '排序',
                    prop: 'sortNum',
                    component: {
                        name: 'el-input-number'
                    }
                }
            ]
        };
    },
    onData(list) {
        return deepTree(list);
    },
    onSelect(item) {
        refresh({
            parentId: item.id
        });
    }
});
// cl-upsert
const Upsert = useUpsert({
    items: [
        {
            label: '名称',
            prop: 'name',
            component: { name: 'el-input', props: { clearable: true } },
            required: true
        },
        {
            label: '排序',
            prop: 'sortNum',
            hook: 'number',
            component: { name: 'el-input-number' }
        },
        {
            label: '图标',
            prop: 'pic',
            component: {
                name: 'cl-upload'
            }
        },
        { label: '状态', prop: 'status', value: 1, component: { name: 'cl-switch' }, flex: false }
    ],
    plugins: [setFocus('name')],
    onSubmit(data, { next }) {
        next({
            ...data,
            parentId: ViewGroup.value?.selected?.id
        });
    }
});
// cl-table
const Table = useTable({
    columns: [
        { type: 'selection' },
        { label: '名称', prop: 'name', align: 'left', minWidth: 200 },
        {
            label: '图标',
            prop: 'pic',
            minWidth: 100,
            component: { name: 'cl-image', props: { size: 40 } }
        },
        { label: '排序', prop: 'sortNum', minWidth: 100, sortable: 'desc' },
        { label: '状态', prop: 'status', minWidth: 100, component: { name: 'cl-switch' } },
        {
            label: '创建时间',
            prop: 'createTime',
            minWidth: 160
        },
        {
            label: '更新时间',
            prop: 'updateTime',
            minWidth: 160
        },
        {
            type: 'op'
        }
    ]
});
// cl-crud
const Crud = useCrud({
    service: service.goods.type
});
// 刷新
function refresh(params?: any) {
    Crud.value?.refresh(params);
}
</script>
src/modules/helper/hooks/ai.ts
New file
@@ -0,0 +1,102 @@
import { module } from '/@/cool';
import { useBase } from '/$/base';
export function useAi() {
    const { api } = module.config('helper');
    const { user } = useBase();
    // 调用流程
    async function invokeFlow(
        label: string,
        params: any,
        streamCb?: ({ isEnd, content }: { isEnd: boolean; content: string }) => void
    ): Promise<any> {
        const stream = !!streamCb;
        let cacheText = '';
        return new Promise((resolve, reject) => {
            fetch(api + '/open/code/gen/data', {
                method: 'POST',
                headers: {
                    Authorization: user.token,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    params,
                    label,
                    stream
                })
            })
                .then(res => {
                    if (res.body) {
                        if (stream) {
                            const reader = res.body.getReader();
                            const decoder = new TextDecoder('utf-8');
                            const stream = new ReadableStream({
                                start(controller) {
                                    function push() {
                                        reader.read().then(({ done, value }) => {
                                            if (done) {
                                                controller.close();
                                                return;
                                            }
                                            let text = decoder.decode(value, { stream: true });
                                            if (streamCb) {
                                                if (cacheText) {
                                                    text = cacheText + text;
                                                }
                                                if (text.indexOf('data:') == 0) {
                                                    text = '\n\n' + text;
                                                }
                                                try {
                                                    const arr = text
                                                        .split(/\n\ndata:/g)
                                                        .filter(Boolean)
                                                        .map(e => JSON.parse(e));
                                                    arr.forEach(streamCb);
                                                    cacheText = '';
                                                } catch (err) {
                                                    cacheText = text;
                                                }
                                            }
                                            controller.enqueue(text);
                                            push();
                                        });
                                    }
                                    push();
                                }
                            });
                            return new Response(stream);
                        } else {
                            return res.json();
                        }
                    }
                })
                .then(res => {
                    if (stream) {
                        return res;
                    }
                    if (res.code == 1000) {
                        resolve(res.data.result);
                    } else {
                        reject(res);
                    }
                })
                .catch(reject);
        });
    }
    return {
        invokeFlow
    };
}
src/modules/helper/hooks/index.ts
@@ -1,2 +1,3 @@
export * from './ai';
export * from './menu';
export * from './plugin';
src/modules/helper/static/svg/arrow-down.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1719536095395" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2583" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M61.44 512C61.44 263.168 263.168 61.44 512 61.44s450.56 201.728 450.56 450.56-201.728 450.56-450.56 450.56S61.44 760.832 61.44 512z m397.312 180.85888c29.61408 31.92832 76.88192 31.92832 106.496 0l199.63904-215.28576 0.90112-1.00352c17.01888-19.456 16.52736-50.44224-1.10592-69.2224-19.29216-20.52096-48.82432-23.81824-71.4752-7.96672l-150.58944 105.49248c-18.67776 13.1072-42.55744 13.1072-61.2352 0L330.1376 398.9504c-22.26176-15.5648-51.3024-12.34944-70.26688 7.84384l-0.55296 0.57344a46.8992 46.8992 0 0 0-0.90112 1.024c-17.14176 19.31264-16.83456 50.29888 0.69632 69.2224l199.65952 215.26528z" p-id="2584"></path></svg>
src/modules/helper/static/svg/code.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1719377496622" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6926" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M213.333 128h85.334v85.333h-85.334v213.334A85.333 85.333 0 0 1 128 512a85.333 85.333 0 0 1 85.333 85.333v213.334h85.334V896h-85.334C167.68 884.48 128 857.6 128 810.667V640a85.333 85.333 0 0 0-85.333-85.333H0v-85.334h42.667A85.333 85.333 0 0 0 128 384V213.333A85.333 85.333 0 0 1 213.333 128m597.334 0A85.333 85.333 0 0 1 896 213.333V384a85.333 85.333 0 0 0 85.333 85.333H1024v85.334h-42.667A85.333 85.333 0 0 0 896 640v170.667A85.333 85.333 0 0 1 810.667 896h-85.334v-85.333h85.334V597.333A85.333 85.333 0 0 1 896 512a85.333 85.333 0 0 1-85.333-85.333V213.333h-85.334V128h85.334M512 640a42.667 42.667 0 0 1 42.667 42.667A42.667 42.667 0 0 1 512 725.333a42.667 42.667 0 0 1-42.667-42.666A42.667 42.667 0 0 1 512 640m-170.667 0A42.667 42.667 0 0 1 384 682.667a42.667 42.667 0 0 1-42.667 42.666 42.667 42.667 0 0 1-42.666-42.666A42.667 42.667 0 0 1 341.333 640m341.334 0a42.667 42.667 0 0 1 42.666 42.667 42.667 42.667 0 0 1-42.666 42.666A42.667 42.667 0 0 1 640 682.667 42.667 42.667 0 0 1 682.667 640z" p-id="6927"></path></svg>
src/modules/helper/static/svg/quick.svg
New file
@@ -0,0 +1,18 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg
    t="1739266182011"
    class="icon"
    viewBox="0 0 1024 1024"
    version="1.1"
    xmlns="http://www.w3.org/2000/svg"
    p-id="16931"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    width="128"
    height="128"
>
    <path
        class="p"
        d="M305.572 958.122c-20.41-0.01-36.95-16.56-36.94-36.97 0-3.35 0.46-6.69 1.36-9.92l84.28-302.19-184.32 1.42a36.055 36.055 0 0 1-30.89-16.24 36.946 36.946 0 0 1-3.6-34.7l193.8-474.36a36.97 36.97 0 0 1 34.22-22.98h362.23c20.42 0.01 36.96 16.57 36.95 36.99 0 4.65-0.88 9.27-2.6 13.6l-117.61 296.37h210.65c20.41 0.01 36.95 16.56 36.94 36.97 0 10.71-4.65 20.88-12.74 27.9l-547.57 475.05a36.875 36.875 0 0 1-24.16 9.06z m97.47-423.35c20.41 0.01 36.95 16.56 36.94 36.97 0 3.35-0.46 6.68-1.36 9.9l-64.45 231.07 379.98-329.68h-166.05c-20.39 0.02-36.94-16.5-36.96-36.89 0-4.68 0.88-9.33 2.61-13.68l117.62-296.39h-283.04l-163.49 400.05 177.9-1.42 0.3 0.07z"
        p-id="16932"
    ></path>
</svg>
src/modules/info/views/banner.vue
New file
@@ -0,0 +1,137 @@
<template>
    <cl-crud ref="Crud">
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <!-- 新增按钮 -->
            <cl-add-btn />
            <!-- 删除按钮 -->
            <cl-multi-delete-btn />
            <cl-flex1 />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
        <!-- 新增、编辑 -->
        <cl-upsert ref="Upsert">
            <template #slot-path="{ scope }">
                <el-row>
                    <el-input
                        v-model="scope.path"
                        placeholder="如:/pages/goods/detail?id="
                        :style="{ marginRight: '10px', flex: 1 }"
                    />
                    <goods-select
                        :ref="setRefs('goodsSelect')"
                        :multiple="false"
                        @change="onGoodsSelectChange"
                    >
                        <el-button @click="refs.goodsSelect?.open">选择商品</el-button>
                    </goods-select>
                </el-row>
            </template>
        </cl-upsert>
    </cl-crud>
</template>
<script lang="ts" name="info-banner" setup>
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import GoodsSelect from "/$/goods/components/select.vue";
const { service, refs, setRefs } = useCool();
// cl-upsert
const Upsert = useUpsert({
    items: [
        {
            label: "图片",
            prop: "pic",
            component: {
                name: "cl-upload",
                props: {
                    size: [120, 230]
                }
            },
            required: true
        },
        {
            label: "描述",
            prop: "description",
            component: { name: "el-input", props: { clearable: true } }
        },
        {
            label: "跳转路径",
            prop: "path",
            component: { name: "slot-path" }
        },
        {
            label: "排序",
            prop: "sortNum",
            hook: "number",
            component: { name: "el-input-number" }
        },
        {
            label: "状态",
            prop: "status",
            flex: false,
            value: 1,
            component: { name: "cl-switch" }
        }
    ]
});
// cl-table
const Table = useTable<Eps.InfoBannerEntity>({
    columns: [
        { type: "selection" },
        {
            label: "图片",
            prop: "pic",
            minWidth: 160,
            component: { name: "cl-image", props: { size: [60, 115] } }
        },
        { label: "描述", prop: "description", minWidth: 160 },
        { label: "跳转路径", prop: "path", minWidth: 160 },
        { label: "排序", prop: "sortNum", minWidth: 100, sortable: "desc" },
        { label: "状态", prop: "status", minWidth: 100, component: { name: "cl-switch" } },
        {
            label: "创建时间",
            prop: "createTime",
            minWidth: 160
        },
        {
            label: "更新时间",
            prop: "updateTime",
            minWidth: 160
        },
        { type: "op", buttons: ["edit", "delete"] }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        service: service.info.banner
    },
    (app) => {
        app.refresh();
    }
);
// 选择商品后
function onGoodsSelectChange(id: number) {
    Upsert.value?.setForm("path", `/pages/goods/detail?id=${id}`);
}
</script>
src/modules/market/views/coupon/info.vue
New file
@@ -0,0 +1,282 @@
<template>
    <cl-crud ref="Crud">
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <!-- 新增按钮 -->
            <cl-add-btn />
            <!-- 删除按钮 -->
            <cl-multi-delete-btn />
            <cl-filter label="状态">
                <cl-select :options="options.status" prop="status" :width="140" />
            </cl-filter>
            <cl-flex1 />
            <!-- 关键字搜索 -->
            <cl-search-key placeholder="搜索标题" />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
        <!-- 新增、编辑 -->
        <cl-upsert ref="Upsert">
            <template #slot-condition="{ scope }">
                <div class="condition">
                    <div class="row">
                        <span>满</span>
                        <el-input-number
                            v-model="scope.condition.fullAmount"
                            :max="10000"
                            :min="scope.amount"
                        />
                        <span>减 {{ scope.amount || 0 }} 元</span>
                    </div>
                </div>
            </template>
        </cl-upsert>
    </cl-crud>
</template>
<script lang="ts" name="market-coupon-info" setup>
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import { reactive } from "vue";
import { isEmpty } from "lodash-es";
import dayjs from "dayjs";
const { service, router } = useCool();
const options = reactive({
    type: [
        {
            label: "满减券",
            value: 0
        }
    ],
    status: [
        {
            label: "启用",
            value: 1
        },
        {
            label: "禁用",
            value: 0
        }
    ]
});
// cl-upsert
const Upsert = useUpsert({
    props: {
        labelWidth: "100px"
    },
    items: [
        {
            label: "类型",
            prop: "type",
            value: 0,
            component: { name: "el-radio-group", options: options.type },
            required: true
        },
        {
            label: "标题",
            prop: "title",
            component: { name: "el-input", props: { clearable: true } },
            required: true
        },
        {
            label: "描述",
            prop: "description",
            component: { name: "el-input", props: { type: "textarea", rows: 4 } },
            required: true
        },
        {
            label: "金额",
            prop: "amount",
            hook: "number",
            component: {
                name: "el-input-number",
                props: {
                    min: 0.01,
                    max: 100000
                }
            },
            required: true,
            span: 12
        },
        {
            label: "有效期",
            prop: "time",
            component: {
                name: "el-date-picker",
                props: {
                    type: "daterange",
                    valueFormat: "YYYY-MM-DD"
                }
            },
            rules: {
                required: true,
                validator(rule, value, callback) {
                    if (isEmpty(value)) {
                        callback(Error("有效期不能为空"));
                    } else {
                        callback();
                    }
                }
            },
            hook: "datetimeRange",
            span: 12
        },
        {
            label: "总数量",
            prop: "num",
            hook: "number",
            value: 100,
            component: { name: "el-input-number" },
            required: true,
            span: 12
        },
        {
            label: "已领取",
            prop: "receivedNum",
            hook: "number",
            value: 0,
            component: { name: "el-input-number" },
            required: true,
            span: 12
        },
        {
            label: "使用条件",
            prop: "condition",
            value: {
                fullAmount: undefined
            },
            component: { name: "slot-condition" },
            rules: {
                required: true,
                validator(rule, value, callback) {
                    if (!value.fullAmount) {
                        callback(Error("请填写满减条件"));
                    } else {
                        callback();
                    }
                }
            }
        },
        {
            label: "状态",
            prop: "status",
            flex: false,
            component: { name: "cl-switch" },
            required: true
        }
    ]
});
// cl-table
const Table = useTable<Eps.MarketCouponInfoEntity>({
    columns: [
        { type: "selection" },
        { label: "标题", prop: "title", minWidth: 200 },
        { label: "描述", prop: "description", showOverflowTooltip: true, minWidth: 200 },
        { label: "类型", prop: "type", minWidth: 120, dict: options.type },
        {
            label: "条件",
            prop: "condition",
            minWidth: 140,
            formatter(row) {
                switch (row.type) {
                    case 0:
                        return `满 ${row.condition?.fullAmount} 减 ${row.amount}`;
                    default:
                        return "无门槛";
                }
            }
        },
        { label: "金额", prop: "amount", minWidth: 120, sortable: "custom" },
        { label: "数量", prop: "num", minWidth: 120, sortable: "custom" },
        { label: "已领取", prop: "receivedNum", minWidth: 120, sortable: "custom" },
        { label: "状态", prop: "status", minWidth: 100, component: { name: "cl-switch" } },
        {
            label: "有效期",
            prop: "startTime",
            minWidth: 160,
            formatter(row) {
                return (
                    dayjs(row.startTime).format("YYYY-MM-DD") +
                    " ~ " +
                    dayjs(row.endTime).format("YYYY-MM-DD")
                );
            }
        },
        {
            label: "创建时间",
            prop: "createTime",
            minWidth: 160,
            component: { name: "cl-date-text" }
        },
        {
            label: "更新时间",
            prop: "updateTime",
            minWidth: 160,
            component: { name: "cl-date-text" }
        },
        {
            type: "op",
            width: 260,
            buttons: [
                "edit",
                "delete",
                {
                    label: "领取列表",
                    onClick({ scope }) {
                        router.push({
                            path: "/market/coupon/user",
                            query: {
                                id: scope.row.id,
                                title: scope.row.title
                            }
                        });
                    }
                }
            ]
        }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        service: service.market.coupon.info
    },
    (app) => {
        app.refresh();
    }
);
</script>
<style lang="scss" scoped>
.condition {
    .row {
        display: flex;
        align-items: center;
        .el-input-number {
            margin: 0 10px;
            width: 200px;
        }
    }
}
</style>
src/modules/market/views/coupon/user.vue
New file
@@ -0,0 +1,129 @@
<template>
    <cl-crud ref="Crud">
        <cl-view-head />
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <!-- 新增按钮 -->
            <cl-add-btn />
            <!-- 删除按钮 -->
            <cl-multi-delete-btn />
            <cl-flex1 />
            <!-- 关键字搜索 -->
            <cl-search-key placeholder="搜索用户昵称" />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
        <!-- 新增、编辑 -->
        <cl-upsert ref="Upsert" />
    </cl-crud>
</template>
<script lang="ts" name="market-coupon-user" setup>
import { useCrud, useTable, useUpsert } from '@cool-vue/crud';
import { useCool } from '/@/cool';
import { reactive } from 'vue';
import UserSelect from '/$/user/components/user-select.vue';
const { service, route, refs, setRefs } = useCool();
const options = reactive({
    status: [
        { label: '未使用', value: 0, type: 'info' },
        { label: '已使用', value: 1, type: 'success' }
    ]
});
// cl-upsert
const Upsert = useUpsert({
    dialog: {
        width: '600px'
    },
    items: [
        {
            label: '选择用户',
            prop: 'userId',
            component: {
                vm: UserSelect,
                ref: setRefs('userSelect'),
                props: {
                    multiple: false
                }
            },
            required: true
        },
        {
            label: '状态',
            prop: 'status',
            component: {
                name: 'el-radio-group',
                options: options.status
            },
            value: 0,
            required: true
        }
    ],
    onOpened(data) {
        if (Upsert.value?.mode != 'add') {
            service.user.info
                .info({
                    id: data.userId
                })
                .then(res => {
                    refs.userSelect.select(res);
                });
        }
    },
    onSubmit(data, { next }) {
        next({
            ...data,
            couponId: route.query.id
        });
    }
});
// cl-table
const Table = useTable({
    columns: [
        { type: 'selection' },
        { label: '用户昵称', prop: 'nickName', minWidth: 140 },
        {
            label: '状态',
            prop: 'status',
            dict: options.status,
            minWidth: 120
        },
        { label: '使用时间', prop: 'useTime', minWidth: 160, sortable: 'custom' },
        {
            label: '创建时间',
            prop: 'createTime',
            minWidth: 160,
            sortable: 'desc'
        },
        { type: 'op', buttons: ['edit', 'delete'] }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        service: service.market.coupon.user
    },
    app => {
        app.refresh({
            couponId: route.query.id
        });
    }
);
</script>
src/modules/monitor/components/site-tree.vue
@@ -13,29 +13,18 @@
        <div class="site-tree__container">
            <el-scrollbar>
                <el-tree
                    v-loading="loading"
                    node-key="a_id"
                    default-expand-all
                    :data="list"
                    :props="{
                <el-tree v-loading="loading" node-key="a_id" default-expand-all :data="list" :props="{
                        label: 'a_name',
                        children: 'children'
                    }"
                    highlight-current
                    :expand-on-click-node="false"
                    @node-click="rowClick"
                >
                }" highlight-current :expand-on-click-node="false" @node-click="rowClick">
                    <template #default="{ node, data }">
                        <div class="site-tree__node">
                            <span
                                class="site-tree__node-label"
                                :class="{
                            <span class="site-tree__node-label" :class="{
                                    'is-active': data.a_id == selectId
                                }"
                            >
                            }">
                                {{ node.label }}
                                <span v-if="!/^\d+$/.test(data.a_id)" style="margin-left:6px;color:#67c23a;font-size:12px;">(站点)</span>
                                <span v-if="!/^\d+$/.test(data.a_id)"
                                    style="margin-left:6px;color:#67c23a;font-size:12px;">(站点)</span>
                            </span>
                        </div>
                    </template>                    
@@ -85,7 +74,7 @@
async function refresh() {
  loading.value = true;
  try {
    const res = await service.basicdata.iot.list();
        const res = await service.basicdata.iot.getSitelist();
    const formatList = res.map(item => ({
      ...item,
      a_id: String(item.a_id),
@@ -135,6 +124,7 @@
    &__op {
        display: flex;
        align-items: center;
        .item {
            display: flex;
            justify-content: center;
@@ -146,6 +136,7 @@
            height: 26px;
            width: 26px;
            color: var(--el-text-color-primary);
            &:hover {
                background-color: var(--el-fill-color-light);
            }
@@ -155,6 +146,7 @@
    &__container {
        height: calc(100% - 40px);
        padding: 10px;
        :deep(.el-tree-node__content) {
            height: 38px;
            border-radius: 4px;
@@ -167,6 +159,7 @@
        height: 100%;
        width: 100%;
        box-sizing: border-box;
        &-label {
            display: flex;
            align-items: center;
@@ -176,6 +169,7 @@
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            &.is-active {
                color: var(--el-color-primary);
            }
src/modules/order/components/discount-item.vue
New file
@@ -0,0 +1,45 @@
<template>
    <div class="discount-item" v-if="scope.discountSource">
        <p class="t1">- {{ scope.discountPrice }}</p>
        <p class="t2">{{ doc }}</p>
    </div>
    <span v-else>-</span>
</template>
<script lang="ts" setup name="order-discount-item">
import { type PropType, computed } from "vue";
const props = defineProps({
    scope: {
        type: Object as PropType<Eps.OrderInfoEntity>,
        default: () => ({})
    }
});
const doc = computed(() => {
    const { info, type } = props.scope.discountSource || {};
    switch (type) {
        case 0:
            return `${info.title}(满 ${info.condition.fullAmount} 元减 ${info.amount} 元)`;
        default:
            return "";
    }
});
</script>
<style lang="scss" scoped>
.discount-item {
    text-align: left;
    .t1 {
        color: red;
    }
    .t2 {
        font-size: 12px;
    }
}
</style>
src/modules/order/components/goods.vue
New file
@@ -0,0 +1,145 @@
<template>
    <div class="order-goods">
        <div class="list">
            <div class="goods" v-for="goods in list" :key="goods.id">
                <p class="name">{{ goods.name }}</p>
                <div class="specs">
                    <div class="item" v-for="item in goods.children" :key="item.id">
                        <cl-image :src="item.cover" :size="60" />
                        <div class="det">
                            <p class="name">{{ item.spec.name }}</p>
                            <p class="flex1"></p>
                            <p class="price">
                                <span>¥{{ item.spec.price }}</span>
                                <span>x{{ item.count }}</span>
                            </p>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
<script setup lang="ts" name="order-goods">
import { isEmpty } from "lodash-es";
import { PropType, computed } from "vue";
interface Item {
    id: number;
    name: string;
    children: Eps.GoodsInfoEntity[];
}
const props = defineProps({
    scope: {
        type: Object as PropType<Eps.OrderInfoEntity & { goodsList?: any[] }>,
        default: () => ({})
    }
});
const list = computed(() => {
    const arr: Item[] = [];
    props.scope.goodsList
        ?.filter((e) => !!e)
        .forEach((e) => {
            // 封面图
            let pics = e.spec.images || [];
            if (isEmpty(pics)) {
                pics = [e.goodsInfo.mainPic];
            }
            e.cover = pics[0];
            const d = arr.find((a) => a.id == e.goodsInfo.id);
            if (d) {
                d.children?.push(e);
            } else {
                arr.push({
                    id: e.goodsInfo.id!,
                    name: e.goodsInfo.title!,
                    children: [e]
                });
            }
        });
    return arr;
});
</script>
<style lang="scss" scoped>
.order-goods {
    .list {
        display: flex;
        flex-wrap: wrap;
        .goods {
            margin-left: 10px;
            & > .name {
                font-size: 14px;
                margin-bottom: 5px;
                padding: 0 2px;
                height: 20px;
                max-width: 200px;
                box-sizing: border-box;
                overflow: hidden;
                display: -webkit-box;
                -webkit-box-orient: vertical;
                -webkit-line-clamp: 1;
            }
            .specs {
                display: flex;
            }
            .item {
                display: flex;
                align-items: center;
                font-size: 12px;
                position: relative;
                border: 1px solid var(--el-bg-color-page);
                border-radius: 6px;
                padding: 10px;
                height: 80px;
                width: 250px;
                margin-right: 10px;
                .cl-image {
                    margin-right: 10px;
                    border-radius: 6px;
                    overflow: hidden;
                }
                .det {
                    display: flex;
                    flex-direction: column;
                    flex: 1;
                    height: 100%;
                    padding: 2px 0;
                    .name {
                        overflow: hidden;
                        display: -webkit-box;
                        -webkit-box-orient: vertical;
                        -webkit-line-clamp: 2;
                    }
                    .flex1 {
                        flex: 1;
                    }
                    .price {
                        display: flex;
                        align-items: center;
                        justify-content: space-between;
                    }
                }
            }
        }
    }
}
</style>
src/modules/order/components/info-item.vue
New file
@@ -0,0 +1,24 @@
<template>
    <div class="info-item">
        <p>标题:{{ scope.title }}</p>
        <p>单号:{{ scope.orderNum }}</p>
    </div>
</template>
<script lang="ts" setup name="order-info-item">
import { type PropType } from "vue";
const props = defineProps({
    scope: {
        type: Object as PropType<Eps.OrderInfoEntity>,
        default: () => ({})
    }
});
</script>
<style lang="scss" scoped>
.info-item {
    text-align: left;
    font-size: 12px;
}
</style>
src/modules/order/components/logistics-item.vue
New file
@@ -0,0 +1,229 @@
<template>
    <div class="logistics-item">
        <div class="row">
            <span>联系电话:</span>
            <span>{{ scope.address.phone }}({{ scope.address.contact }})</span>
        </div>
        <div class="row">
            <span>所在地区:</span>
            <span>
                {{ scope.address.province }}{{ scope.address.city }}{{ scope.address.district }}
            </span>
        </div>
        <div class="row">
            <span>详细地址:</span>
            <span>
                {{ scope.address.address }}
            </span>
        </div>
        <div class="btns" v-if="scope.status != 0">
            <el-button size="small" @click="toDeliver()" v-if="[1, 2].includes(scope.status!)">{{
                scope.status == 1 ? "发货" : "编辑"
            }}</el-button>
            <el-button size="small" @click="open" v-if="scope.logistics">查看物流</el-button>
        </div>
        <!-- 发货 -->
        <cl-form ref="Form" />
        <!-- 物流信息 -->
        <cl-dialog v-model="visible" title="物流信息" height="60vh">
            <div class="info" v-loading="loading">
                <template v-if="info?.expName">
                    <el-descriptions :column="1" :style="{ marginBottom: '10px' }">
                        <el-descriptions-item label="快递名称:">{{
                            info?.expName
                        }}</el-descriptions-item>
                        <el-descriptions-item label="物流单号:">{{
                            info?.number
                        }}</el-descriptions-item>
                    </el-descriptions>
                    <el-timeline>
                        <el-timeline-item
                            v-for="(item, index) in info?.list"
                            :key="index"
                            hollow
                            :timestamp="item.time"
                        >
                            {{ item.status }}
                        </el-timeline-item>
                    </el-timeline>
                </template>
                <el-empty :image-size="80" description="暂无物流信息" v-else />
            </div>
        </cl-dialog>
    </div>
</template>
<script lang="ts" setup name="order-logistics-item">
import { useForm } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import { PropType, ref } from "vue";
import { ElMessage } from "element-plus";
import { useDict } from "/$/dict";
import { isString } from "lodash-es";
const props = defineProps({
    scope: {
        type: Object as PropType<Eps.OrderInfoEntity>,
        default: () => ({})
    },
    disabled: Boolean
});
const { service } = useCool();
const { dict } = useDict();
const Form = useForm();
// 物流信息
const info = ref<{
    expName: string;
    number: string;
    list: {
        time: string;
        status: string;
    }[];
}>();
// 是否可见
const visible = ref(false);
// 信息加载中
const loading = ref(false);
// 查看物流
async function open() {
    visible.value = true;
    loading.value = true;
    await service.order.info
        .logistics({
            orderId: props.scope.id
        })
        .then((res) => {
            info.value = undefined;
            if (isString(res)) {
                ElMessage.error(res);
            } else {
                info.value = res;
            }
        })
        .catch((err) => {
            ElMessage.error(err.message);
        });
    loading.value = false;
}
// 发货
function toDeliver() {
    const { num, company } = props.scope.logistics || {};
    const isEdit = props.scope.status != 1;
    Form.value?.open({
        title: isEdit ? "编辑单号" : "订单发货",
        width: "500px",
        props: {
            labelPosition: "top"
        },
        form: {
            num,
            company
        },
        items: [
            {
                label: "物流公司",
                prop: "company",
                required: true,
                component: {
                    name: "el-select",
                    options: dict.get("logisticsCompany"),
                    props: {
                        filterable: true
                    }
                }
            },
            {
                label: "物流单号",
                prop: "num",
                required: true,
                component: {
                    name: "el-input",
                    props: {
                        clearable: true
                    }
                }
            }
        ],
        on: {
            submit(data, { close, done }) {
                if (data.company == "sf") {
                    if (!data.num.includes(":")) {
                        data.num += `:${props.scope.address.phone.slice(-4)}`;
                    }
                }
                service.order.info[isEdit ? "update" : "deliver"]({
                    [isEdit ? "id" : "orderId"]: props.scope.id,
                    logistics: data
                })
                    .then(() => {
                        ElMessage.success("保存成功");
                        close();
                        // 更新状态为待收货
                        props.scope.status = 2;
                        // 更新物流信息
                        props.scope.logistics = data;
                    })
                    .catch((err) => {
                        ElMessage.error(err.message);
                        done();
                    });
            }
        }
    });
}
</script>
<style lang="scss" scoped>
.logistics-item {
    text-align: left;
    font-size: 12px;
    position: relative;
    .icon {
        position: absolute;
        right: -5px;
        top: 0;
        cursor: pointer;
        font-size: 16px;
        color: var(--el-color-danger);
    }
    .row {
        display: flex;
        & > span {
            &:first-child {
                flex-shrink: 0;
            }
        }
    }
    .btns {
        margin-top: 4px;
    }
}
</style>
src/modules/order/components/pay-item.vue
New file
@@ -0,0 +1,20 @@
<template>
    <div class="pay-item">
        <el-tag v-if="scope.payType == 0" effect="dark" type="info">未支付</el-tag>
        <el-tag v-else-if="scope.payType == 1" effect="dark" color="#2aae67">微信</el-tag>
        <el-tag v-else-if="scope.payType == 2" effect="dark" color="#1678FF">支付宝</el-tag>
        <p :style="{ marginTop: '5px' }">{{ scope.payTime }}</p>
    </div>
</template>
<script lang="ts" setup name="order-pay-item">
import { type PropType } from "vue";
const props = defineProps({
    scope: {
        type: Object as PropType<Eps.OrderInfoEntity>,
        default: () => ({})
    }
});
</script>
src/modules/order/components/remark-item.vue
New file
@@ -0,0 +1,26 @@
<template>
    <div class="remark-item">
        <p v-if="scope.remark">用户下单:{{ scope.remark }}</p>
        <p v-if="scope.closeRemark">取消订单:{{ scope.closeRemark }}</p>
        <p v-if="scope.refund?.refuseReason">拒绝退款:{{ scope.refund?.refuseReason }}</p>
        <p v-if="scope.refund?.reason">申请退款:{{ scope.refund?.reason }}</p>
    </div>
</template>
<script lang="ts" setup name="order-remark-item">
import { type PropType } from "vue";
const props = defineProps({
    scope: {
        type: Object as PropType<Eps.OrderInfoEntity>,
        default: () => ({})
    }
});
</script>
<style lang="scss" scoped>
.remark-item {
    text-align: left;
    font-size: 12px;
}
</style>
src/modules/order/components/user-item.vue
New file
@@ -0,0 +1,20 @@
<template>
    <div class="order-user-item">
        <cl-avatar :src="scope.avatarUrl" :size="36"></cl-avatar>
        <span>{{ scope.nickName }}</span>
    </div>
</template>
<script lang="ts" setup name="order-user-item">
const props = defineProps({
    scope: null
});
</script>
<style lang="scss" scoped>
.order-user-item {
    .cl-avatar {
        margin-bottom: 5px;
    }
}
</style>
src/modules/order/dict/index.ts
New file
@@ -0,0 +1,16 @@
export const OrderStatus = [
    { label: "待付款", value: 0, type: "info" },
    { label: "待发货", value: 1, type: "warning" },
    { label: "待收货", value: 2 },
    { label: "待评价", value: 3, type: "success" },
    { label: "交易完成", value: 4, type: "success" },
    { label: "退款中", value: 5, type: "danger" },
    { label: "已退款", value: 6, type: "danger" },
    { label: "已关闭", value: 7, type: "danger" }
];
export const PayType = [
    { label: "待支付", value: 0, type: "info" },
    { label: "微信", value: 1, color: "#2aae67" },
    { label: "支付宝", value: 2, color: "#1678FF" }
];
src/modules/order/views/info.vue
New file
@@ -0,0 +1,165 @@
<template>
    <cl-crud ref="Crud">
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <cl-filter label="支付方式">
                <cl-select :options="options.payType" prop="payType" :width="140" />
            </cl-filter>
            <cl-filter label="订单状态">
                <cl-select :options="options.status" prop="status" :width="140" />
            </cl-filter>
            <cl-filter label="创建时间">
                <cl-date-picker type="datetimerange" />
            </cl-filter>
            <cl-flex1 />
            <!-- 关键字搜索 -->
            <cl-search-key placeholder="搜索订单号、标题、用户昵称" />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table" show-summary :summary-method="onSummaryMethod" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
    </cl-crud>
</template>
<script lang="ts" name="order-info" setup>
import { useCrud, useTable } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import { reactive, ref } from "vue";
import { OrderStatus, PayType } from "../dict";
import OrderUserItem from "../components/user-item.vue";
import OrderLogisticsItem from "../components/logistics-item.vue";
import OrderPayItem from "../components/pay-item.vue";
import OrderRemarkItem from "../components/remark-item.vue";
import OrderInfoItem from "../components/info-item.vue";
import OrderDiscountItem from "../components/discount-item.vue";
import OrderGoods from "../components/goods.vue";
const { service } = useCool();
// 选项
const options = reactive({
    payType: PayType,
    status: OrderStatus
});
// 合计数据
const subData = ref({
    totalCount: 0,
    totalPrice: 0
});
// cl-table
const Table = useTable<Eps.OrderInfoEntity>({
    contextMenu: ["delete"],
    columns: [
        {
            type: "expand",
            label: "商品",
            width: 60,
            component: {
                vm: OrderGoods
            }
        },
        {
            label: "订单信息",
            prop: "orderNum",
            minWidth: 160,
            component: {
                vm: OrderInfoItem
            }
        },
        {
            label: "用户信息",
            prop: "user",
            minWidth: 120,
            component: {
                vm: OrderUserItem
            }
        },
        {
            label: "金额(元)",
            prop: "price",
            minWidth: 120,
            sortable: "custom"
        },
        {
            label: "优惠说明",
            prop: "discount",
            minWidth: 160,
            component: {
                vm: OrderDiscountItem
            }
        },
        {
            label: "付款信息",
            prop: "pay",
            minWidth: 160,
            component: {
                vm: OrderPayItem
            }
        },
        {
            label: "物流信息",
            prop: "logistics",
            minWidth: 250,
            component: {
                vm: OrderLogisticsItem
            }
        },
        {
            label: "备注",
            prop: "remark",
            minWidth: 200,
            component: {
                vm: OrderRemarkItem
            }
        },
        {
            label: "创建时间",
            prop: "createTime",
            minWidth: 160,
            sortable: "desc"
        },
        {
            label: "状态",
            prop: "status",
            dict: options.status,
            minWidth: 100,
            fixed: "right"
        }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        service: service.order.info,
        async onRefresh(params, { next }) {
            const res = await next(params);
            // 设置合计数据
            subData.value = res.subData;
        }
    },
    (app) => {
        app.refresh();
    }
);
function onSummaryMethod() {
    return ["合计", "", "", subData.value.totalPrice + " 元"];
}
</script>
src/modules/order/views/refund.vue
New file
@@ -0,0 +1,272 @@
<template>
    <cl-crud ref="Crud">
        <cl-row>
            <!-- 刷新按钮 -->
            <cl-refresh-btn />
            <cl-filter label="支付方式">
                <cl-select :options="options.payType" prop="payType" :width="140" />
            </cl-filter>
            <cl-filter label="订单状态">
                <cl-select :options="options.status" prop="status" :width="140" />
            </cl-filter>
            <cl-filter label="申请时间">
                <cl-date-picker prop="refundApply" type="datetimerange" />
            </cl-filter>
            <cl-flex1 />
            <!-- 关键字搜索 -->
            <cl-search-key placeholder="搜索订单号、标题、用户昵称" />
        </cl-row>
        <cl-row>
            <!-- 数据表格 -->
            <cl-table ref="Table" show-summary :summary-method="onSummaryMethod" />
        </cl-row>
        <cl-row>
            <cl-flex1 />
            <!-- 分页控件 -->
            <cl-pagination />
        </cl-row>
        <!-- 退款 -->
        <cl-form ref="Form" />
    </cl-crud>
</template>
<script lang="ts" name="order-refund" setup>
import { useCrud, useForm, useTable } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import { reactive, ref } from "vue";
import { ElMessage } from "element-plus";
import OrderUserItem from "../components/user-item.vue";
import OrderLogisticsItem from "../components/logistics-item.vue";
import OrderPayItem from "../components/pay-item.vue";
import OrderRemarkItem from "../components/remark-item.vue";
import OrderInfoItem from "../components/info-item.vue";
import OrderDiscountItem from "../components/discount-item.vue";
import OrderGoods from "../components/goods.vue";
const { service } = useCool();
const options = reactive({
    payType: [
        { label: "微信", value: 1, color: "#2aae67" },
        { label: "支付宝", value: 2, color: "1678FF" }
    ],
    status: [
        { label: "申请中", value: 0, type: "info" },
        { label: "已退款", value: 1, type: "success" },
        { label: "已拒绝", value: 2, type: "danger" }
    ]
});
// 合计数据
const subData = ref({
    totalCount: 0,
    totalPrice: 0
});
// cl-table
const Table = useTable({
    contextMenu: [],
    columns: [
        {
            type: "expand",
            label: "商品",
            width: 60,
            component: {
                vm: OrderGoods
            }
        },
        {
            label: "订单信息",
            prop: "orderNum",
            minWidth: 160,
            component: {
                vm: OrderInfoItem
            }
        },
        {
            label: "用户信息",
            prop: "user",
            minWidth: 120,
            component: {
                vm: OrderUserItem
            }
        },
        {
            label: "金额(元)",
            prop: "price",
            minWidth: 120,
            sortable: "custom"
        },
        {
            label: "优惠说明",
            prop: "discount",
            minWidth: 160,
            component: {
                vm: OrderDiscountItem
            }
        },
        {
            label: "付款信息",
            prop: "pay",
            minWidth: 160,
            component: {
                vm: OrderPayItem
            }
        },
        {
            label: "物流信息",
            prop: "logistics",
            minWidth: 250,
            component: {
                vm: OrderLogisticsItem
            }
        },
        {
            label: "备注",
            prop: "remark",
            minWidth: 200,
            component: {
                vm: OrderRemarkItem
            }
        },
        {
            label: "申请时间",
            prop: "refund.applyTime",
            minWidth: 160
        },
        {
            label: "状态",
            prop: "refund",
            dict: options.status,
            formatter(row) {
                return row.refund?.status;
            },
            minWidth: 100,
            fixed: "right"
        },
        {
            type: "op",
            width: 100,
            buttons({ scope }) {
                return [
                    {
                        label: "退款",
                        type: "danger",
                        hidden: !(
                            service.order.info._permission.refundHandle &&
                            scope.row.refund.status == 0
                        ),
                        onClick() {
                            toRefund(scope.row);
                        }
                    }
                ];
            }
        }
    ]
});
// cl-crud
const Crud = useCrud(
    {
        service: service.order.info,
        async onRefresh(params, { next }) {
            const res = await next(params);
            // 设置合计数据
            subData.value = res.subData;
        }
    },
    (app) => {
        app.refresh({
            status: [5, 6]
        });
    }
);
function refresh(params?: any) {
    Crud.value?.refresh(params);
}
function onSummaryMethod() {
    return ["合计", "", "", subData.value.totalPrice + " 元"];
}
// 发起退款
const Form = useForm();
function toRefund(item: Eps.OrderInfoEntity) {
    Form.value?.open({
        title: "发起退款",
        width: "500px",
        props: {
            labelWidth: "80px"
        },
        op: {
            saveButtonText: "提交"
        },
        items: [
            {
                label: "退款状态",
                prop: "action",
                value: 1,
                required: true,
                component: {
                    name: "el-radio-group",
                    options: [
                        {
                            label: "同意",
                            value: 1
                        },
                        {
                            label: "拒绝",
                            value: 0
                        }
                    ]
                }
            },
            {
                label: "拒绝原因",
                prop: "refuseReason",
                required: true,
                hidden({ scope }) {
                    return scope.action != 0;
                },
                component: {
                    name: "el-input",
                    props: {
                        type: "textarea",
                        rows: 4
                    }
                }
            }
        ],
        on: {
            submit(data, { done, close }) {
                service.order.info
                    .refundHandle({
                        ...data,
                        orderId: item.id,
                        amount: item.price
                    })
                    .then(() => {
                        ElMessage.success("操作成功");
                        refresh();
                        close();
                    })
                    .catch((err) => {
                        ElMessage.error(err.message);
                        done();
                    });
            }
        }
    });
}
</script>
src/modules/shop/views/recycleorderitem.vue
@@ -37,8 +37,8 @@
import { useCool } from "/@/cool";
import { useI18n } from "vue-i18n";
import { reactive } from "vue";
import OrderSelect from "/$/recycle/components/order-select.vue";
import ItemSelect from "/$/recycle/components/item-select.vue";
import OrderSelect from "../components/order-select.vue";
import ItemSelect from "../components/item-select.vue";
const { service } = useCool();
const { t } = useI18n();
src/modules/user/locales/zh-cn.json
@@ -16,15 +16,5 @@
  "状态": "状态",
  "创建时间": "创建时间",
  "选择用户": "选择用户",
  "姓名": "姓名",
  "学生": "学生",
  "老师": "老师",
  "学院名称": "学院名称",
  "专业名称": "专业名称",
  "班级名称": "班级名称",
  "学院ID": "学院ID",
  "专业ID": "专业ID",
  "班级ID": "班级ID",
  "碳积分总额":"碳积分总额",
  "唯一标识":"唯一标识"
  "姓名": "姓名"
}