This commit is contained in:
14
.dockerignore
Normal file
14
.dockerignore
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.git
|
||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
|
docker/ # 排除 docker 配置文件夹本身
|
||||||
|
.editorconfig
|
||||||
|
.eslintrc.js
|
||||||
|
.prettierrc
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
26
.env.development
Normal file
26
.env.development
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# 请求协议 http or https(建议线上配置证书后,使用https)
|
||||||
|
VITE_PREFIX=http://
|
||||||
|
|
||||||
|
# 内网 中的 Nacos 配置中心 ip:端口 | 域名
|
||||||
|
#VITE_NACOS_ADDRESS=cluster.nacos.com
|
||||||
|
VITE_NACOS_ADDRESS=192.168.139.84:8848
|
||||||
|
|
||||||
|
|
||||||
|
# 注册到 Nacos 的服务名称(微前端模块名称
|
||||||
|
VITE_SERVER_NAME=app-product
|
||||||
|
# 注册到 Nacos 的端口,也是前端运行的端口
|
||||||
|
VITE_SERVER_PORT=1301
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# 说明:项目要调用哪些 微前端 模块
|
||||||
|
# 格式:VITE_MODEL_XXXX_NAME=微前端模块名称(模块 的 开发者提供)
|
||||||
|
# 作用:通过名称从Nacos配置中心发现模块地址和端口,给 federation 实时加载
|
||||||
|
#
|
||||||
|
|
||||||
|
# shared 共享资源模块
|
||||||
|
VITE_MODEL_SHARED_NAME=app-shared
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
23
.env.production
Normal file
23
.env.production
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# 请求协议 http or https(建议线上配置证书后,使用https)
|
||||||
|
VITE_PREFIX=http://
|
||||||
|
|
||||||
|
# 内网 中的 Nacos 配置中心 ip:端口 | 域名
|
||||||
|
#VITE_NACOS_ADDRESS=cluster.nacos.com
|
||||||
|
VITE_NACOS_ADDRESS=192.168.139.84:8848
|
||||||
|
|
||||||
|
|
||||||
|
# 注册到 Nacos 的服务名称(微前端模块名称
|
||||||
|
VITE_SERVER_NAME=app-product
|
||||||
|
# 注册到 Nacos 的端口,也是前端运行的端口
|
||||||
|
VITE_SERVER_PORT=1301
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# 说明:项目要调用哪些 微前端 模块
|
||||||
|
# 格式:VITE_MODEL_XXXX_NAME=微前端模块名称(模块 的 开发者提供)
|
||||||
|
# 作用:通过名称从Nacos配置中心发现模块地址和端口,给 federation 实时加载
|
||||||
|
#
|
||||||
|
|
||||||
|
# shared 共享资源模块
|
||||||
|
VITE_MODEL_SHARED_NAME=app-shared
|
||||||
|
|
||||||
75
.gitea/workflows/build.yml
Normal file
75
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
name: build and push
|
||||||
|
|
||||||
|
# 当代码推送到 main 分支时触发
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 拉取最新代码
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: 设置 Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: 登录到自己的 Docker 仓库
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: registry.bmycode.help
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
|
- name: 构建并推送镜像
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
registry.bmycode.help/app-product:latest
|
||||||
|
registry.bmycode.help/app-product:${{ github.sha }}
|
||||||
|
|
||||||
|
|
||||||
|
- name: 替换镜像标签为 github.sha
|
||||||
|
run: |
|
||||||
|
sed -i "s|{{IMAGE_TAG}}|${{ github.sha }}|g" app-product.yml
|
||||||
|
|
||||||
|
- name: 上传 app-product.yml 到前端集群管理机
|
||||||
|
uses: appleboy/scp-action@v1
|
||||||
|
with:
|
||||||
|
host: ${{ vars.SSH_MANAGE }}
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
password: ${{ secrets.PASSWORD }}
|
||||||
|
port: ${{ secrets.PORT }}
|
||||||
|
source: "app-product.yml"
|
||||||
|
target: "/home/bmy/"
|
||||||
|
|
||||||
|
- name: 登录前端集群,拉镜像
|
||||||
|
uses: appleboy/ssh-action@v1
|
||||||
|
with:
|
||||||
|
# 统一登录三台前端集群的linux,统一拉取刚推到仓库的最新镜像
|
||||||
|
# 解决 Swarm 无法控制 工作机拉取镜像的问题!
|
||||||
|
host: ${{ vars.SSH_HOST_LIST }}
|
||||||
|
sync: true
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
password: ${{ secrets.PASSWORD }}
|
||||||
|
port: ${{ secrets.PORT }}
|
||||||
|
script: |
|
||||||
|
# 拉取刚打包的最新镜像
|
||||||
|
docker pull registry.bmycode.help/app-product:${{ github.sha }}
|
||||||
|
|
||||||
|
- name: 管理节点部署到 Swarm
|
||||||
|
uses: appleboy/ssh-action@v1
|
||||||
|
with:
|
||||||
|
host: ${{ vars.SSH_MANAGE }}
|
||||||
|
sync: true
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
password: ${{ secrets.PASSWORD }}
|
||||||
|
port: ${{ secrets.PORT }}
|
||||||
|
script: |
|
||||||
|
# 切换到管理员,要不然没权限在服务器执行 docker 命令,部署到 Swarm 集群
|
||||||
|
sudo docker stack deploy -c /home/bmy/app-product.yml app-product
|
||||||
22
Dockerfile
Normal file
22
Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
FROM node:24-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json ./
|
||||||
|
|
||||||
|
# 直接使用 npm install 并解决依赖问题
|
||||||
|
RUN npm install --no-audit --no-fund --legacy-peer-deps
|
||||||
|
|
||||||
|
# 拷贝源码并构建
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 【前端修改】如果要以子路径 /app-product/ 部署,请用 base 指定前缀,同 .env中VITE_SERVER_NAME
|
||||||
|
RUN npx vite build --base=/app-product/
|
||||||
|
|
||||||
|
FROM nginx:alpine
|
||||||
|
# 构建产物放到 /usr/share/nginx/html/app-product
|
||||||
|
# nginx.conf 也需要修改
|
||||||
|
COPY --from=builder /app/dist /usr/share/nginx/html/app-product
|
||||||
|
# 覆盖 Nginx 配置,支持 SPA 子路径
|
||||||
|
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
156
README.md
Normal file
156
README.md
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
|
||||||
|
## app-product 商品模块(微前端模块)
|
||||||
|
|
||||||
|
从项目中拆分出来,用于演示的微前端模块,主要实现 商品相关功能。本案例演示中提供:
|
||||||
|
|
||||||
|
- 调用 微前端 app-shared 模块
|
||||||
|
- 调接口,获取商品首页的数据和展示
|
||||||
|
- 调接口,点击商品列表进入商品详情页
|
||||||
|
|
||||||
|
|
||||||
|
## 运行
|
||||||
|
|
||||||
|
下面文档分为**开发**和**上线**两部分进行说明!**默认前端已经在电脑上安装过 Docker 环境!!**
|
||||||
|
|
||||||
|
> 想要【开发阶段运行】,必须 先参考下面 【上线】-【任务1】配置好两个前端自己的环境变量文件!很简单!
|
||||||
|
|
||||||
|
### 开发
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装依赖
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 开发阶段 运行 项目
|
||||||
|
# PS:注意此命令运行的项目,【无法】被其他 微前端模块 使用,仅用于本地独立开发,独立测试
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
|
||||||
|
# 启动 实时打包 & 实时预览
|
||||||
|
# PS:注意此命令运行的项目,【可以】被其他 微前端模块 调用,可用于本地开发和微前端联合开发
|
||||||
|
npm run dep
|
||||||
|
```
|
||||||
|
|
||||||
|
### 上线
|
||||||
|
|
||||||
|
代码 到 仓库的 **main 分支** 后会触发 全程自动化,实现自动打包前端代码,构建镜像,自动部署到公司内网的Linux服务器集群中!
|
||||||
|
提交到其他分支不触发自动化操作!具体什么规则,以 技术负责人 说的为准!请遵守规则!
|
||||||
|
|
||||||
|
|
||||||
|
**根目录核心文件说明**
|
||||||
|
|
||||||
|
- `app-product.yml` - Dcoker Swarm 集群部署配置【不准动】
|
||||||
|
- `docker-compose.yml` - 开发阶段容器编排【可选,不要就删除】
|
||||||
|
- `Dockerfile` - 打包构建前端代码为镜像【根据需求改动】
|
||||||
|
- `nginx.conf` - 镜像内前端被访问的Nginx配置【根据需求改动】
|
||||||
|
- `act-runner.yml` - gitea action 的 runner【根据需求改动】
|
||||||
|
|
||||||
|
前端工作:
|
||||||
|
|
||||||
|
前期大致工作内容如下,改好后,以后就不用再动了,很简单:
|
||||||
|
|
||||||
|
- 任务1: 编辑 前端 .env.development 和 .env.production 环境变量(只需修改一次)
|
||||||
|
- 任务2: 确认或编辑 Dockerfile,nginx.conf
|
||||||
|
- 任务3: 编辑 act-runner.yml
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
详细展开说明:
|
||||||
|
|
||||||
|
**任务1:**
|
||||||
|
|
||||||
|
主要修改两个环境变量文件 `.env.development`,`.env.production` 中的:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
|
||||||
|
# 找技术负责人要
|
||||||
|
# 情况1: 【内网模块】填写 Nacos 内网 ip 或 域名 即可
|
||||||
|
# 情况2: 【外网模块】填写 traefik 公网 ip 或 域名 即可
|
||||||
|
# .env.development, .env.production 中 可以不一样!
|
||||||
|
VITE_NACOS_ADDRESS=????
|
||||||
|
|
||||||
|
|
||||||
|
# 这个参数很重要,前端可以自定义填写,填写后不允许再改,填完给 技术负责人 即可
|
||||||
|
# 微前端应用的名称,遵守 app-xxxxx
|
||||||
|
# .env.development, .env.production 中 必须一样!
|
||||||
|
VITE_SERVER_NAME=app-???
|
||||||
|
|
||||||
|
#【ps】其他参数看环境变量内说明
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**任务2:**
|
||||||
|
|
||||||
|
主要修改两个 `Dockerfile`,`nginx.conf`,改起来也很简单:
|
||||||
|
|
||||||
|
|
||||||
|
Dockerfile 修改教程:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
|
||||||
|
|
||||||
|
# 将 VITE_SERVER_NAME 替换为 上一步设置的 VITE_SERVER_NAME 的 数值 即可
|
||||||
|
RUN npx vite build --base=/VITE_SERVER_NAME/
|
||||||
|
|
||||||
|
# 将 VITE_SERVER_NAME 替换为 上一步设置的 VITE_SERVER_NAME 的 数值 即可
|
||||||
|
COPY --from=builder /app/dist /usr/share/nginx/html/VITE_SERVER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
nginx.conf 修改教程:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 看仔细了,有三处 VITE_SERVER_NAME 需要替换成 值!!
|
||||||
|
location /VITE_SERVER_NAME/ {
|
||||||
|
alias /usr/share/nginx/html/VITE_SERVER_NAME/;
|
||||||
|
try_files $uri $uri/ /VITE_SERVER_NAME/index.html;
|
||||||
|
add_header 'Access-Control-Allow-Origin' '*';
|
||||||
|
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||||
|
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
||||||
|
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**任务3:**
|
||||||
|
|
||||||
|
act-runner.yml 是 Gitea Action 的核心,Gitea会将Job委托给Runner来运行,**所以我们前端本地必须要先运行 act-runner.yml 文件**,这样在我们提交代码的时候 Gitea Action 的自动构建才会运行!!
|
||||||
|
|
||||||
|
但是,运行 act-runner.yml 之前,需要先对 act-runner.yml 文件进行简单的配置:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
|
||||||
|
# 核心 需要改动的配置
|
||||||
|
# 可以直接让 仓库 的管理员 给你把下面参数之间配置好,当然你也可以自己配
|
||||||
|
|
||||||
|
# GITEA_INSTANCE_URL=这个前端项目模块的 git仓库地址
|
||||||
|
# 格式:协议://域名 即可,例如:https://git.bmycode.help 或 https://gitee.com 等
|
||||||
|
|
||||||
|
# 项目仓库首页-设置-Actions-Runner 右上角可复制令牌
|
||||||
|
# GITEA_RUNNER_REGISTRATION_TOKEN=注册令牌,来源三选一:实例,组织,全存储库
|
||||||
|
environment:
|
||||||
|
- GITEA_INSTANCE_URL=https://git.bmycode.help
|
||||||
|
- GITEA_RUNNER_REGISTRATION_TOKEN=WFiO5JDwz7CHnk7fMGJYMcSITU81CLXuI7Ofg10e
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
改好后,在**项目文件夹根目录**打开终端,通过下面命令运行 act-runner.yml 文件:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose -f act-runner.yml up -d
|
||||||
|
|
||||||
|
# 运行后,如果 在 docker desktop 的容器中找到 名字叫:gitea-act-runner 的容器,查看日志:
|
||||||
|
|
||||||
|
# 有下面这行,则表示 runner 成功启动!!docker desktop 可以关闭了或者放后台,不用管了!
|
||||||
|
# level=debug msg="Successfully pinged the Gitea instance server"
|
||||||
|
|
||||||
|
# 如果出问题了,找 技术负责人!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**到这一步,上线 配置已经搞完,很简单吧!剩下的,你提交代码到仓库就行,项目会自动构建,自动打包发布到公司内网集群中部署和运行,不用你管了!!**
|
||||||
|
|
||||||
|
|
||||||
17
act-runner.yml
Normal file
17
act-runner.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
gitea-actions-runner:
|
||||||
|
image: gitea/act_runner:latest
|
||||||
|
container_name: gitea-act-runner
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- runner-data:/data
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
- GITEA_INSTANCE_URL=https://git.bmycode.help
|
||||||
|
- GITEA_RUNNER_REGISTRATION_TOKEN=WFiO5JDwz7CHnk7fMGJYMcSITU81CLXuI7Ofg10e
|
||||||
|
- GITEA_RUNNER_LABELS=ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
runner-data:
|
||||||
39
app-product.yml
Normal file
39
app-product.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
version: "3.8" # Docker Compose 文件版本,3.8 支持 Swarm 模式的所有功能
|
||||||
|
|
||||||
|
services:
|
||||||
|
app-product:
|
||||||
|
image: registry.bmycode.help/app-product:{{IMAGE_TAG}}
|
||||||
|
ports:
|
||||||
|
- target: 80 # 容器内部端口
|
||||||
|
published: 1301 # 宿主机对外暴露端口
|
||||||
|
protocol: tcp # 协议类型
|
||||||
|
mode: ingress # 模式:使用路由网格
|
||||||
|
deploy:
|
||||||
|
replicas: 3 # 副本数量,在集群中运行 3 个容器实例
|
||||||
|
placement:
|
||||||
|
max_replicas_per_node: 1 # 每个 Swarm 节点最多运行 1 个副本,确保高可用性
|
||||||
|
restart_policy:
|
||||||
|
condition: any # 重启条件:任何情况下容器退出都重启
|
||||||
|
delay: 5s # 重启延迟 5 秒
|
||||||
|
max_attempts: 3 # 最大重启尝试次数
|
||||||
|
update_config: # 滚动更新配置
|
||||||
|
parallelism: 1 # 并行更新数量:一次只更新 1 个容器
|
||||||
|
delay: 10s # 更新间隔:每个容器更新后等待 10 秒再更新下一个
|
||||||
|
failure_action: rollback # 更新失败时自动回滚到上一版本
|
||||||
|
order: start-first # 更新顺序:先启动新容器,确认健康后再停止旧容器
|
||||||
|
rollback_config: # 回滚配置
|
||||||
|
parallelism: 1 # 回滚时并行数量
|
||||||
|
delay: 5s # 回滚延迟
|
||||||
|
resources: # 资源限制
|
||||||
|
limits: # 资源上限
|
||||||
|
cpus: "0.50" # 最多使用 0.5 个 CPU 核心
|
||||||
|
memory: "256M" # 最多使用 256MB 内存
|
||||||
|
reservations: # 资源预留
|
||||||
|
cpus: "0.25" # 预留 0.25 个 CPU 核心
|
||||||
|
memory: "128M" # 预留 128MB 内存
|
||||||
|
networks:
|
||||||
|
- frontend # 连接到 frontend 网络
|
||||||
|
|
||||||
|
networks:
|
||||||
|
frontend: # 定义 frontend 网络
|
||||||
|
driver: overlay # 使用 overlay 网络驱动,支持跨主机通信
|
||||||
1
dist/assets/ProductDetail-C2SXoWRu.js
vendored
Normal file
1
dist/assets/ProductDetail-C2SXoWRu.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{importShared as u}from"./__federation_fn_import-E6wRZccp.js";import{product as d}from"./productModule-C57DerbR.js";const s="_ProductDetail_16njr_1",m="_title_16njr_1",r={ProductDetail:s,title:m},{createTextVNode:o,createVNode:e}=await u("vue"),{defineComponent:p,onMounted:P,reactive:_}=await u("vue"),{useRoute:v}=await u("vue-router"),w=p({setup(){const i=v(),c=d.createProductApplicationService(),t=_({item:{},id:0});P(()=>{if(!i.query.id)throw new Error("缺少id参数");t.id=Number(i.query.id),console.log("id: ",t.id),a()});const a=async()=>{t.item=await c.getProductInfo(t.id),console.log("state.item: ",t.item)};return()=>e("div",{class:r.ProductDetail},[e("h1",{class:r.title},[o("商品领域模块 - 商品详情页面")]),e("hr",null,null),e("p",null,[o("商品id:"),t.id]),t.item.name,e("ul",null,[Object.entries(t.item).map(([l,n])=>e("li",null,[l,o(": "),n]))])])}});export{w as default};
|
||||||
1
dist/assets/ProductDetail-rjscZczN.css
vendored
Normal file
1
dist/assets/ProductDetail-rjscZczN.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
._ProductDetail_16njr_1 ._title_16njr_1{color:#00f}
|
||||||
1
dist/assets/ProductList-BnGJM5LT.js
vendored
Normal file
1
dist/assets/ProductList-BnGJM5LT.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{importShared as r}from"./__federation_fn_import-E6wRZccp.js";import{__federation_method_getRemote as i}from"./__federation_expose_ProductDomain-J98eKK33.js";import{product as p}from"./productModule-C57DerbR.js";const _="_ProductList_1t3dn_1",E="_test_1t3dn_1",s={ProductList:_,test:E},{createVNode:t,createTextVNode:e}=await r("vue"),{defineComponent:m,onMounted:C,reactive:f}=await r("vue"),{useRouter:h}=await r("vue-router"),v=await i("shared","./element-plus");let{ElCard:P,ElIcon:F,Icons:L}=v;const x=m({setup(){const a=h(),n=p.createProductApplicationService(),o=f({list:[]});C(()=>{c()});const c=async()=>{o.list=await n.getProductList(),console.log("state.list: ",o.list)},d=()=>t("div",{class:"card-header"},[t(F,{size:15},{default:()=>[t(L.Position,null,null)]}),t("span",null,[e("Card 头部")])]),l=()=>t("div",null,[e("Footer 底部")]);return()=>t("div",{class:s.ProductList},[t("h1",{class:s.test},[e("商品领域模块 - 商品列表页面3")]),t("hr",null,null),t("h2",null,[e("从Deno 后端 加载的数据,点击进详情")]),t(P,null,{header:()=>d(),footer:()=>l(),default:()=>o.list.map(u=>t("p",{key:u.id,onClick:()=>{a.push({name:"ProductDetail",query:{id:u.id}})}},[u.id,e(" | "),u.name,e(" | ¥"),u.price]))})])}});export{x as default};
|
||||||
1
dist/assets/ProductList-CV-Muv1j.css
vendored
Normal file
1
dist/assets/ProductList-CV-Muv1j.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
._ProductList_1t3dn_1 ._test_1t3dn_1{color:var(--theme-color)}
|
||||||
1
dist/assets/__federation_expose_ProductDomain-J98eKK33.js
vendored
Normal file
1
dist/assets/__federation_expose_ProductDomain-J98eKK33.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var o=(e=>(e.PriceNotNegativein="商品 价格 不能为负数",e.StockNotNegativein="商品 库存 不能为负数",e.StockIsGreaterThanZero="减少的库存数量必须大于0",e.LowStock="库存不足",e.IdNotNull="商品Id不能为空",e))(o||{});class I{constructor(t){this.productRepository=t}async getProductList(){return await this.productRepository.findAll()}async getProductInfo(t){if(!t)throw new Error(o.IdNotNull);return await this.productRepository.findById(t)}}class c{_id;_name;_description;_price;_imageUrl;_stock;_categoryId;constructor(t,r,i,_,a,s,n){this._id=t,this._name=r,this._description=i,this._price=_,this._imageUrl=a,this._stock=s,this._categoryId=n}get id(){return this._id}get name(){return this._name}set name(t){this._name=t}get description(){return this._description}set description(t){this._description=t}get price(){return this._price}set price(t){if(t<0)throw new Error(o.PriceNotNegativein);this._price=t}get imageUrl(){return this._imageUrl}set imageUrl(t){this._imageUrl=t}get stock(){return this._stock}set stock(t){if(t<0)throw new Error(o.StockNotNegativein);this._stock=t}get categoryId(){return this._categoryId}set categoryId(t){this._categoryId=t}hasStock(){return this._stock>0}reduceStock(t){if(t<=0)throw new Error(o.StockIsGreaterThanZero);if(this._stock<t)throw new Error(o.LowStock);this._stock-=t}toDTO(){return{id:this._id,name:this._name,description:this._description,price:this._price,imageUrl:this._imageUrl,stock:this._stock,categoryId:this._categoryId}}static fromDTO(t){return new c(t.id,t.name,t.description,t.price,t.imageUrl,t.stock,t.categoryId)}}const m={shared:{url:"http://192.168.31.101:1333/app-shared/assets/remoteEntry.js",format:"esm",from:"vite"}},u={};function d(e,t){return g(e).then(r=>()=>r)}function l(e,t){const r=Object.assign(e,t);for(const i of Object.keys(r))typeof r[i]=="object"&&typeof t[i]=="object"&&(r[i]=l(r[i],t[i]));return r}const f=e=>l({vue:{undefined:{get:()=>d(new URL("__federation_shared_vue-CmZhWQPe.js",import.meta.url).href),loaded:1}},"vue-router":{undefined:{get:()=>d(new URL("__federation_shared_vue-router-CUrgpNf2.js",import.meta.url).href),loaded:1}},pinia:{undefined:{get:()=>d(new URL("__federation_shared_pinia-o7RAmTGs.js",import.meta.url).href),loaded:1}}},(globalThis.__federation_shared__||{}).default||{});async function g(e){return u[e]??=import(e),u[e]}async function y(e){const t=m[e];if(t.inited)return t.lib;if(["esm","systemjs"].includes(t.format))return new Promise((r,i)=>{Promise.resolve(t.url).then(a=>{import(a).then(s=>{if(!t.inited){const n=f();s.init(n),t.lib=s,t.lib.init(n),t.inited=!0}r(t.lib)}).catch(i)})})}function w(e,t){return y(e).then(r=>r.get(t).then(i=>i()))}class h{static apiUrlList={productsListUrl:"/productsList",productsInfoUrl:"/productsInfo"}}const k=await w("shared","./infra");let{http:p}=k;class U{async findAll(){return(await p.get({url:h.apiUrlList.productsListUrl})).map(i=>c.fromDTO(i))}async findById(t){const r=await p.get({url:h.apiUrlList.productsInfoUrl,data:{id:t}});return c.fromDTO(r)}}export{o as ErrorText,U as HttpProductRepository,c as Product,I as ProductApplicationService,w as __federation_method_getRemote};
|
||||||
2
dist/assets/__federation_expose_ProductRouter-BB_8fJdP.js
vendored
Normal file
2
dist/assets/__federation_expose_ProductRouter-BB_8fJdP.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ProductList-BnGJM5LT.js","assets/__federation_fn_import-E6wRZccp.js","assets/__federation_expose_ProductDomain-J98eKK33.js","assets/productModule-C57DerbR.js","assets/ProductList-CV-Muv1j.css","assets/ProductDetail-C2SXoWRu.js","assets/ProductDetail-rjscZczN.css"])))=>i.map(i=>d[i]);
|
||||||
|
const E="modulepreload",P=function(i){return"/app-product/"+i},u={},d=function(m,s,v){let l=Promise.resolve();if(s&&s.length>0){let p=function(e){return Promise.all(e.map(o=>Promise.resolve(o).then(c=>({status:"fulfilled",value:c}),c=>({status:"rejected",reason:c}))))};document.getElementsByTagName("link");const n=document.querySelector("meta[property=csp-nonce]"),t=n?.nonce||n?.getAttribute("nonce");l=p(s.map(e=>{if(e=P(e),e in u)return;u[e]=!0;const o=e.endsWith(".css"),c=o?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${e}"]${c}`))return;const r=document.createElement("link");if(r.rel=o?"stylesheet":E,o||(r.as="script"),r.crossOrigin="",r.href=e,t&&r.setAttribute("nonce",t),document.head.appendChild(r),o)return new Promise((f,h)=>{r.addEventListener("load",f),r.addEventListener("error",()=>h(new Error(`Unable to preload CSS for ${e}`)))})}))}function a(n){const t=new Event("vite:preloadError",{cancelable:!0});if(t.payload=n,window.dispatchEvent(t),!t.defaultPrevented)throw n}return l.then(n=>{for(const t of n||[])t.status==="rejected"&&a(t.reason);return m().catch(a)})},_=[{path:"/Products",name:"ProductList",component:()=>d(()=>import("./ProductList-BnGJM5LT.js"),__vite__mapDeps([0,1,2,3,4])),meta:{title:"商品列表"}},{path:"/ProductDetail",name:"ProductDetail",component:()=>d(()=>import("./ProductDetail-C2SXoWRu.js"),__vite__mapDeps([5,1,3,2,6])),meta:{title:"商品详情"}}];export{_ as productRouter};
|
||||||
1
dist/assets/__federation_fn_import-E6wRZccp.js
vendored
Normal file
1
dist/assets/__federation_fn_import-E6wRZccp.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
const L="[0-9A-Za-z-]+",P=`(?:\\+(${L}(?:\\.${L})*))`,R="0|[1-9]\\d*",_="[0-9]+",Z="\\d*[a-zA-Z-][a-zA-Z0-9-]*",S=`(?:${_}|${Z})`,N=`(?:-?(${S}(?:\\.${S})*))`,X=`(?:${R}|${Z})`,k=`(?:-(${X}(?:\\.${X})*))`,w=`${R}|x|X|\\*`,d=`[v=\\s]*(${w})(?:\\.(${w})(?:\\.(${w})(?:${k})?${P}?)?)?`,D=`^\\s*(${d})\\s+-\\s+(${d})\\s*$`,J=`(${_})\\.(${_})\\.(${_})`,Q=`[v=\\s]*${J}${N}?${P}?`,y="((?:<|>)?=?)",U=`(\\s*)${y}\\s*(${Q}|${d})`,F="(?:~>?)",W=`(\\s*)${F}\\s+`,K="(?:\\^)",Y=`(\\s*)${K}\\s+`,A="(<|>)?=?\\s*\\*",j=`^${K}${d}$`,m=`(${R})\\.(${R})\\.(${R})`,ee=`v?${m}${k}?${P}?`,re=`^${F}${d}$`,ne=`^${y}\\s*${d}$`,se=`^${y}\\s*(${ee})$|^$`,te="^\\s*>=\\s*0.0.0\\s*$";function o(e){return new RegExp(e)}function c(e){return!e||e.toLowerCase()==="x"||e==="*"}function B(...e){return s=>e.reduce((t,r)=>r(t),s)}function E(e){return e.match(o(se))}function z(e,s,t,r){const n=`${e}.${s}.${t}`;return r?`${n}-${r}`:n}function ie(e){return e.replace(o(D),(s,t,r,n,i,$,u,f,a,l,V,p)=>(c(r)?t="":c(n)?t=`>=${r}.0.0`:c(i)?t=`>=${r}.${n}.0`:t=`>=${t}`,c(a)?f="":c(l)?f=`<${+a+1}.0.0-0`:c(V)?f=`<${a}.${+l+1}.0-0`:p?f=`<=${a}.${l}.${V}-${p}`:f=`<=${f}`,`${t} ${f}`.trim()))}function $e(e){return e.replace(o(U),"$1$2$3")}function ue(e){return e.replace(o(W),"$1~")}function ce(e){return e.replace(o(Y),"$1^")}function fe(e){return e.trim().split(/\s+/).map(s=>s.replace(o(j),(t,r,n,i,$)=>c(r)?"":c(n)?`>=${r}.0.0 <${+r+1}.0.0-0`:c(i)?r==="0"?`>=${r}.${n}.0 <${r}.${+n+1}.0-0`:`>=${r}.${n}.0 <${+r+1}.0.0-0`:$?r==="0"?n==="0"?`>=${r}.${n}.${i}-${$} <${r}.${n}.${+i+1}-0`:`>=${r}.${n}.${i}-${$} <${r}.${+n+1}.0-0`:`>=${r}.${n}.${i}-${$} <${+r+1}.0.0-0`:r==="0"?n==="0"?`>=${r}.${n}.${i} <${r}.${n}.${+i+1}-0`:`>=${r}.${n}.${i} <${r}.${+n+1}.0-0`:`>=${r}.${n}.${i} <${+r+1}.0.0-0`)).join(" ")}function ae(e){return e.trim().split(/\s+/).map(s=>s.replace(o(re),(t,r,n,i,$)=>c(r)?"":c(n)?`>=${r}.0.0 <${+r+1}.0.0-0`:c(i)?`>=${r}.${n}.0 <${r}.${+n+1}.0-0`:$?`>=${r}.${n}.${i}-${$} <${r}.${+n+1}.0-0`:`>=${r}.${n}.${i} <${r}.${+n+1}.0-0`)).join(" ")}function le(e){return e.split(/\s+/).map(s=>s.trim().replace(o(ne),(t,r,n,i,$,u)=>{const f=c(n),a=f||c(i),l=a||c($);return r==="="&&l&&(r=""),u="",f?r===">"||r==="<"?"<0.0.0-0":"*":r&&l?(a&&(i=0),$=0,r===">"?(r=">=",a?(n=+n+1,i=0,$=0):(i=+i+1,$=0)):r==="<="&&(r="<",a?n=+n+1:i=+i+1),r==="<"&&(u="-0"),`${r+n}.${i}.${$}${u}`):a?`>=${n}.0.0${u} <${+n+1}.0.0-0`:l?`>=${n}.${i}.0${u} <${n}.${+i+1}.0-0`:t})).join(" ")}function oe(e){return e.trim().replace(o(A),"")}function pe(e){return e.trim().replace(o(te),"")}function b(e,s){return e=+e||e,s=+s||s,e>s?1:e===s?0:-1}function de(e,s){const{preRelease:t}=e,{preRelease:r}=s;if(t===void 0&&r)return 1;if(t&&r===void 0)return-1;if(t===void 0&&r===void 0)return 0;for(let n=0,i=t.length;n<=i;n++){const $=t[n],u=r[n];if($!==u)return $===void 0&&u===void 0?0:$?u?b($,u):-1:1}return 0}function v(e,s){return b(e.major,s.major)||b(e.minor,s.minor)||b(e.patch,s.patch)||de(e,s)}function x(e,s){return e.version===s.version}function Re(e,s){switch(e.operator){case"":case"=":return x(e,s);case">":return v(e,s)<0;case">=":return x(e,s)||v(e,s)<0;case"<":return v(e,s)>0;case"<=":return x(e,s)||v(e,s)>0;case void 0:return!0;default:return!1}}function Ve(e){return B(fe,ae,le,oe)(e)}function ve(e){return B(ie,$e,ue,ce)(e.trim()).split(/\s+/).join(" ")}function _e(e,s){if(!e)return!1;const n=ve(s).split(" ").map(p=>Ve(p)).join(" ").split(/\s+/).map(p=>pe(p)),i=E(e);if(!i)return!1;const[,$,,u,f,a,l]=i,V={version:z(u,f,a,l),major:u,minor:f,patch:a,preRelease:l?.split(".")};for(const p of n){const g=E(p);if(!g)return!1;const[,H,,O,q,I,h]=g,M={operator:H,version:z(O,q,I,h),major:O,minor:q,patch:I,preRelease:h?.split(".")};if(!Re(M,V))return!1}return!0}const C={},T=Object.create(null);async function he(e,s="default"){return T[e]?new Promise(t=>t(T[e])):await be(e,s)||Te(e)}async function be(e,s){let t=null;if(globalThis?.__federation_shared__?.[s]?.[e]){const r=globalThis.__federation_shared__[s][e],n=C[e]?.requiredVersion;if(!!n){const $=Object.keys(r).find(u=>_e(u,n));$?t=await(await r[$].get())():console.log(`provider support ${e}(${$}) is not satisfied requiredVersion(\${moduleMap[name].requiredVersion})`)}else{const $=Object.keys(r)[0];t=await(await r[$].get())()}}if(t)return G(t,e)}async function Te(e){if(C[e]?.import){let s=await(await C[e].get())();return G(s,e)}else console.error("consumer config import=false,so cant use callback shared module")}function G(e,s){return typeof e.default=="function"?(Object.keys(e).forEach(t=>{t!=="default"&&(e.default[t]=e[t])}),T[s]=e.default,e.default):(e.default&&(e=Object.assign({},e.default,e)),T[s]=e,e)}export{he as importShared,Te as importSharedLocal,be as importSharedRuntime};
|
||||||
1
dist/assets/index-KMVTlfvo.js
vendored
Normal file
1
dist/assets/index-KMVTlfvo.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{importShared as l}from"./__federation_fn_import-E6wRZccp.js";(function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))c(e);new MutationObserver(e=>{for(const t of e)if(t.type==="childList")for(const i of t.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&c(i)}).observe(document,{childList:!0,subtree:!0});function s(e){const t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?t.credentials="include":e.crossOrigin==="anonymous"?t.credentials="omit":t.credentials="same-origin",t}function c(e){if(e.ep)return;e.ep=!0;const t=s(e);fetch(e.href,t)}})();const u={BASE_URL:"/app-product/",DEV:!1,MODE:"production",PROD:!0,SSR:!1,VITE_MODEL_SHARED_NAME:"app-shared",VITE_NACOS_ADDRESS:"192.168.139.84:8848",VITE_PREFIX:"http://",VITE_SERVER_NAME:"app-product",VITE_SERVER_PORT:"1301"},{defineComponent:a,onMounted:p,Fragment:d,createTextVNode:o,createVNode:r}=await l("vue"),f=a({setup(){return p(()=>{console.log("import.meta.env: ",u)}),()=>r(d,null,[r("div",{class:"order-federation-placeholder"},[r("h2",null,[o("商品-联邦模块")]),r("p",null,[o("此页面仅用于 商品模块 的单独开发和调试,可临时引入组件进行本地测试,实际运行时由 主应用 加载本项目代码")]),r("h2",null,[o("本模块包含的功能:")]),r("ul",null,[r("li",null,[o("1:获取商品首页的数据和展示")]),r("li",null,[o("2:点击商品进入详情页")])]),r("p",null,null)])])}}),{createApp:m}=await l("vue"),{createPinia:_}=await l("pinia");m(f).use(_()).mount("#app");
|
||||||
1
dist/assets/productModule-C57DerbR.js
vendored
Normal file
1
dist/assets/productModule-C57DerbR.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{ProductApplicationService as r,HttpProductRepository as t}from"./__federation_expose_ProductDomain-J98eKK33.js";const o={bindProductRepository:()=>new t,createProductApplicationService:()=>new r(o.bindProductRepository())};export{o as product};
|
||||||
1
dist/assets/remoteEntry.js
vendored
Normal file
1
dist/assets/remoteEntry.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
const p={},b=new Set(["Module","__esModule","default","_export_sfc"]);let g={"./ProductDomain":()=>(y([],!1,"./ProductDomain"),D("/app-product/assets/__federation_expose_ProductDomain-J98eKK33.js").then(e=>Object.keys(e).every(r=>b.has(r))?()=>e.default:()=>e)),"./ProductRouter":()=>(y([],!1,"./ProductRouter"),D("/app-product/assets/__federation_expose_ProductRouter-BB_8fJdP.js").then(e=>Object.keys(e).every(r=>b.has(r))?()=>e.default:()=>e))};const m={},y=(e,r,c)=>{const s=import.meta.url;if(typeof s>"u"){console.warn('The remote style takes effect only when the build.target option in the vite.config.ts file is higher than that of "es2020".');return}const i=s.substring(0,s.lastIndexOf("remoteEntry.js")),_='/app-product/';'assets',e.forEach(a=>{let o="";const l=_||i;if(l){const n={trailing:t=>t.endsWith("/")?t.slice(0,-1):t,leading:t=>t.startsWith("/")?t.slice(1):t},E=t=>t.startsWith("http")||t.startsWith("//"),f=n.trailing(l),u=n.leading(a),h=n.trailing(i);E(l)?o=[f,u].filter(Boolean).join("/"):h.includes(f)?o=[h,u].filter(Boolean).join("/"):o=[h+f,u].filter(Boolean).join("/")}else o=a;if(r){const n="css__app-product__"+c;window[n]=window[n]||[],window[n].push(o);return}if(o in m)return;m[o]=!0;const d=document.createElement("link");d.rel="stylesheet",d.href=o,document.head.appendChild(d)})};async function D(e){return p[e]??=import(e),p[e]}const w=e=>{if(!g[e])throw new Error("Can not find remote module "+e);return g[e]()},T=e=>{globalThis.__federation_shared__=globalThis.__federation_shared__||{},Object.entries(e).forEach(([r,c])=>{for(const[s,i]of Object.entries(c)){const _=i.scope||"default";globalThis.__federation_shared__[_]=globalThis.__federation_shared__[_]||{};const a=globalThis.__federation_shared__[_];(a[r]=a[r]||{})[s]=i}})};export{y as dynamicLoadingCss,w as get,T as init};
|
||||||
17
dist/index.html
vendored
Normal file
17
dist/index.html
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Vite App</title>
|
||||||
|
<script type="module" crossorigin src="/app-product/assets/index-KMVTlfvo.js"></script>
|
||||||
|
<link rel="modulepreload" crossorigin href="/app-product/assets/__federation_fn_import-E6wRZccp.js">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
4949
dist/stats.html
vendored
Normal file
4949
dist/stats.html
vendored
Normal file
File diff suppressed because one or more lines are too long
13
docker-compose.yml
Normal file
13
docker-compose.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
app-product:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: docker/Dockerfile
|
||||||
|
|
||||||
|
image: registry.bmycode.help/app-product:latest
|
||||||
|
container_name: app-product
|
||||||
|
ports:
|
||||||
|
- "1301:80"
|
||||||
|
restart: unless-stopped
|
||||||
16
index.html
Normal file
16
index.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Vite App</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
14
nginx.conf
Normal file
14
nginx.conf
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name *.ddd.com;
|
||||||
|
|
||||||
|
|
||||||
|
location /app-product/ {
|
||||||
|
alias /usr/share/nginx/html/app-product/;
|
||||||
|
try_files $uri $uri/ /app-product/index.html;
|
||||||
|
add_header 'Access-Control-Allow-Origin' '*';
|
||||||
|
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||||
|
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
||||||
|
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
|
||||||
|
}
|
||||||
|
}
|
||||||
21
node_modules/.bin/browserslist
generated
vendored
Executable file
21
node_modules/.bin/browserslist
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/browserslist@4.26.3/node_modules/browserslist/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/browserslist@4.26.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/browserslist@4.26.3/node_modules/browserslist/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/browserslist@4.26.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/browserslist@4.26.3/node_modules/browserslist/cli.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/browserslist@4.26.3/node_modules/browserslist/cli.js" "$@"
|
||||||
|
fi
|
||||||
21
node_modules/.bin/lessc
generated
vendored
Executable file
21
node_modules/.bin/lessc
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/less@4.4.1/node_modules/less/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/less@4.4.1/node_modules/less/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/less@4.4.1/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/less@4.4.1/node_modules/less/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/less@4.4.1/node_modules/less/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/less@4.4.1/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/less@4.4.1/node_modules/less/bin/lessc" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/less@4.4.1/node_modules/less/bin/lessc" "$@"
|
||||||
|
fi
|
||||||
21
node_modules/.bin/npm-run-all
generated
vendored
Executable file
21
node_modules/.bin/npm-run-all
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/npm-run-all/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/npm-run-all/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/npm-run-all/index.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/npm-run-all/index.js" "$@"
|
||||||
|
fi
|
||||||
21
node_modules/.bin/npm-run-all2
generated
vendored
Executable file
21
node_modules/.bin/npm-run-all2
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/npm-run-all/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/npm-run-all/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/npm-run-all/index.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/npm-run-all/index.js" "$@"
|
||||||
|
fi
|
||||||
21
node_modules/.bin/rollup
generated
vendored
Executable file
21
node_modules/.bin/rollup
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup@4.52.3/node_modules/rollup/dist/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup@4.52.3/node_modules/rollup/dist/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup@4.52.3/node_modules/rollup/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup@4.52.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup@4.52.3/node_modules/rollup/dist/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup@4.52.3/node_modules/rollup/dist/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup@4.52.3/node_modules/rollup/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup@4.52.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/rollup@4.52.3/node_modules/rollup/dist/bin/rollup" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/rollup@4.52.3/node_modules/rollup/dist/bin/rollup" "$@"
|
||||||
|
fi
|
||||||
21
node_modules/.bin/rollup-plugin-visualizer
generated
vendored
Executable file
21
node_modules/.bin/rollup-plugin-visualizer
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup-plugin-visualizer@6.0.3_rollup@4.52.3/node_modules/rollup-plugin-visualizer/dist/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup-plugin-visualizer@6.0.3_rollup@4.52.3/node_modules/rollup-plugin-visualizer/dist/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup-plugin-visualizer@6.0.3_rollup@4.52.3/node_modules/rollup-plugin-visualizer/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup-plugin-visualizer@6.0.3_rollup@4.52.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup-plugin-visualizer@6.0.3_rollup@4.52.3/node_modules/rollup-plugin-visualizer/dist/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup-plugin-visualizer@6.0.3_rollup@4.52.3/node_modules/rollup-plugin-visualizer/dist/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup-plugin-visualizer@6.0.3_rollup@4.52.3/node_modules/rollup-plugin-visualizer/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/rollup-plugin-visualizer@6.0.3_rollup@4.52.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/rollup-plugin-visualizer@6.0.3_rollup@4.52.3/node_modules/rollup-plugin-visualizer/dist/bin/cli.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/rollup-plugin-visualizer@6.0.3_rollup@4.52.3/node_modules/rollup-plugin-visualizer/dist/bin/cli.js" "$@"
|
||||||
|
fi
|
||||||
21
node_modules/.bin/run-p
generated
vendored
Executable file
21
node_modules/.bin/run-p
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/run-p/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/run-p/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/run-p/index.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/run-p/index.js" "$@"
|
||||||
|
fi
|
||||||
21
node_modules/.bin/run-s
generated
vendored
Executable file
21
node_modules/.bin/run-s
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/run-s/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/run-s/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/npm-run-all2@8.0.4/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/run-s/index.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2/bin/run-s/index.js" "$@"
|
||||||
|
fi
|
||||||
21
node_modules/.bin/tsc
generated
vendored
Executable file
21
node_modules/.bin/tsc
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsc" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsc" "$@"
|
||||||
|
fi
|
||||||
21
node_modules/.bin/tsserver
generated
vendored
Executable file
21
node_modules/.bin/tsserver
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/typescript@5.9.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsserver" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsserver" "$@"
|
||||||
|
fi
|
||||||
21
node_modules/.bin/vite
generated
vendored
Executable file
21
node_modules/.bin/vite
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/vite@7.1.7_@types+node@24.6.1_less@4.4.1/node_modules/vite/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/vite@7.1.7_@types+node@24.6.1_less@4.4.1/node_modules/vite/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/vite@7.1.7_@types+node@24.6.1_less@4.4.1/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/vite@7.1.7_@types+node@24.6.1_less@4.4.1/node_modules/vite/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/vite@7.1.7_@types+node@24.6.1_less@4.4.1/node_modules/vite/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/vite@7.1.7_@types+node@24.6.1_less@4.4.1/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/vite@7.1.7_@types+node@24.6.1_less@4.4.1/node_modules/vite/bin/vite.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/vite@7.1.7_@types+node@24.6.1_less@4.4.1/node_modules/vite/bin/vite.js" "$@"
|
||||||
|
fi
|
||||||
21
node_modules/.bin/vue-tsc
generated
vendored
Executable file
21
node_modules/.bin/vue-tsc
generated
vendored
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$NODE_PATH" ]; then
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/vue-tsc@3.1.0_typescript@5.9.3/node_modules/vue-tsc/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/vue-tsc@3.1.0_typescript@5.9.3/node_modules/vue-tsc/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/vue-tsc@3.1.0_typescript@5.9.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules"
|
||||||
|
else
|
||||||
|
export NODE_PATH="/Users/bmy/Desktop/DDD/node_modules/.pnpm/vue-tsc@3.1.0_typescript@5.9.3/node_modules/vue-tsc/bin/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/vue-tsc@3.1.0_typescript@5.9.3/node_modules/vue-tsc/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/vue-tsc@3.1.0_typescript@5.9.3/node_modules:/Users/bmy/Desktop/DDD/node_modules/.pnpm/node_modules:$NODE_PATH"
|
||||||
|
fi
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/vue-tsc@3.1.0_typescript@5.9.3/node_modules/vue-tsc/bin/vue-tsc.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../../../../node_modules/.pnpm/vue-tsc@3.1.0_typescript@5.9.3/node_modules/vue-tsc/bin/vue-tsc.js" "$@"
|
||||||
|
fi
|
||||||
1
node_modules/@element-plus/icons-vue
generated
vendored
Symbolic link
1
node_modules/@element-plus/icons-vue
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../node_modules/.pnpm/@element-plus+icons-vue@2.3.2_vue@3.5.22_typescript@5.9.3_/node_modules/@element-plus/icons-vue
|
||||||
1
node_modules/@tsconfig/node22
generated
vendored
Symbolic link
1
node_modules/@tsconfig/node22
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../node_modules/.pnpm/@tsconfig+node22@22.0.2/node_modules/@tsconfig/node22
|
||||||
1
node_modules/@types/node
generated
vendored
Symbolic link
1
node_modules/@types/node
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../node_modules/.pnpm/@types+node@24.6.1/node_modules/@types/node
|
||||||
1
node_modules/@vitejs/plugin-vue
generated
vendored
Symbolic link
1
node_modules/@vitejs/plugin-vue
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../node_modules/.pnpm/@vitejs+plugin-vue@6.0.1_vite@7.1.7_@types+node@24.6.1_less@4.4.1__vue@3.5.22_typescript@5.9.3_/node_modules/@vitejs/plugin-vue
|
||||||
1
node_modules/@vitejs/plugin-vue-jsx
generated
vendored
Symbolic link
1
node_modules/@vitejs/plugin-vue-jsx
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../node_modules/.pnpm/@vitejs+plugin-vue-jsx@5.1.1_vite@7.1.7_@types+node@24.6.1_less@4.4.1__vue@3.5.22_typescript@5.9.3_/node_modules/@vitejs/plugin-vue-jsx
|
||||||
1
node_modules/@vue/tsconfig
generated
vendored
Symbolic link
1
node_modules/@vue/tsconfig
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../node_modules/.pnpm/@vue+tsconfig@0.8.1_typescript@5.9.3_vue@3.5.22_typescript@5.9.3_/node_modules/@vue/tsconfig
|
||||||
1
node_modules/element-plus
generated
vendored
Symbolic link
1
node_modules/element-plus
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/element-plus@2.11.4_vue@3.5.22_typescript@5.9.3_/node_modules/element-plus
|
||||||
1
node_modules/less
generated
vendored
Symbolic link
1
node_modules/less
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/less@4.4.1/node_modules/less
|
||||||
1
node_modules/npm-run-all2
generated
vendored
Symbolic link
1
node_modules/npm-run-all2
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/npm-run-all2@8.0.4/node_modules/npm-run-all2
|
||||||
1
node_modules/pinia
generated
vendored
Symbolic link
1
node_modules/pinia
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/pinia@3.0.3_typescript@5.9.3_vue@3.5.22_typescript@5.9.3_/node_modules/pinia
|
||||||
1
node_modules/rollup-plugin-visualizer
generated
vendored
Symbolic link
1
node_modules/rollup-plugin-visualizer
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/rollup-plugin-visualizer@6.0.3_rollup@4.52.3/node_modules/rollup-plugin-visualizer
|
||||||
1
node_modules/typescript
generated
vendored
Symbolic link
1
node_modules/typescript
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript
|
||||||
1
node_modules/vite
generated
vendored
Symbolic link
1
node_modules/vite
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/vite@7.1.7_@types+node@24.6.1_less@4.4.1/node_modules/vite
|
||||||
1
node_modules/vite-federation
generated
vendored
Symbolic link
1
node_modules/vite-federation
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/vite-federation@3.4.4/node_modules/vite-federation
|
||||||
1
node_modules/vite-plugin-nacos
generated
vendored
Symbolic link
1
node_modules/vite-plugin-nacos
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/vite-plugin-nacos@1.1.3/node_modules/vite-plugin-nacos
|
||||||
1
node_modules/vite-plugin-vue-devtools
generated
vendored
Symbolic link
1
node_modules/vite-plugin-vue-devtools
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/vite-plugin-vue-devtools@8.0.2_vite@7.1.7_@types+node@24.6.1_less@4.4.1__vue@3.5.22_typescript@5.9.3_/node_modules/vite-plugin-vue-devtools
|
||||||
1
node_modules/vue
generated
vendored
Symbolic link
1
node_modules/vue
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/vue@3.5.22_typescript@5.9.3/node_modules/vue
|
||||||
1
node_modules/vue-router
generated
vendored
Symbolic link
1
node_modules/vue-router
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/vue-router@4.5.1_vue@3.5.22_typescript@5.9.3_/node_modules/vue-router
|
||||||
1
node_modules/vue-tsc
generated
vendored
Symbolic link
1
node_modules/vue-tsc
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/.pnpm/vue-tsc@3.1.0_typescript@5.9.3/node_modules/vue-tsc
|
||||||
39
package.json
Normal file
39
package.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "app-product",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"dep": "concurrently \"vite build --watch\" \"sleep 5 && npm run preview\"",
|
||||||
|
"preview": "vite preview --port 1301 --strictPort",
|
||||||
|
"build": "vite build",
|
||||||
|
"type-check": "vue-tsc --build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"less": "^4.4.1",
|
||||||
|
"pinia": "^3.0.3",
|
||||||
|
"vite-plugin-nacos": "^1.1.3",
|
||||||
|
"vue": "^3.5.22",
|
||||||
|
"vue-router": "^4.5.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
|
"@tsconfig/node22": "^22.0.2",
|
||||||
|
"@types/node": "^24.6.1",
|
||||||
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
|
"@vitejs/plugin-vue-jsx": "^5.1.1",
|
||||||
|
"@vue/tsconfig": "^0.8.1",
|
||||||
|
"element-plus": "^2.11.4",
|
||||||
|
"npm-run-all2": "^8.0.4",
|
||||||
|
"rollup-plugin-visualizer": "^6.0.3",
|
||||||
|
"typescript": "~5.9.3",
|
||||||
|
"vite": "^7.1.7",
|
||||||
|
"vite-federation": "^3.4.4",
|
||||||
|
"vite-plugin-vue-devtools": "^8.0.2",
|
||||||
|
"vue-tsc": "^3.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/App.tsx
Normal file
27
src/App.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { defineComponent, onMounted } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
console.log("import.meta.env: ", import.meta.env)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<>
|
||||||
|
<div class="order-federation-placeholder">
|
||||||
|
<h2>商品-联邦模块</h2>
|
||||||
|
<p>此页面仅用于 商品模块 的单独开发和调试,可临时引入组件进行本地测试,实际运行时由 主应用
|
||||||
|
加载本项目代码</p>
|
||||||
|
|
||||||
|
<h2>本模块包含的功能:</h2>
|
||||||
|
<ul>
|
||||||
|
<li>1:获取商品首页的数据和展示</li>
|
||||||
|
<li>2:点击商品进入详情页</li>
|
||||||
|
</ul>
|
||||||
|
<p></p>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ErrorText } from "../../domain/enums/ErrorEnums";
|
||||||
|
import { ProductRepository } from "../../ports/repositories/ProductRepository";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品应用服务 - 协调领域对象完成业务用例
|
||||||
|
* 应用服务是领域层的外观,封装了领域逻辑的调用
|
||||||
|
*/
|
||||||
|
export class ProductApplicationService {
|
||||||
|
// 依赖注入仓储接口,而不是具体实现
|
||||||
|
constructor(private productRepository: ProductRepository) { }
|
||||||
|
|
||||||
|
async getProductList() {
|
||||||
|
return await this.productRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProductInfo(id: number) {
|
||||||
|
if (!id) {
|
||||||
|
throw new Error(ErrorText.IdNotNull)
|
||||||
|
}
|
||||||
|
return await this.productRepository.findById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/domains/product/di/productModule.ts
Normal file
38
src/domains/product/di/productModule.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// 从商品联邦模块导入领域层资源
|
||||||
|
import {ProductRepository} from "@domains/product/ports/repositories/ProductRepository";
|
||||||
|
import {HttpProductRepository} from "@domains/product/infr/api/HttpProductRepository";
|
||||||
|
import {ProductApplicationService} from "@domains/product/application/services/ProductApplicationService";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品领域的依赖注入配置
|
||||||
|
* 将 接口 与 具体实现 绑定
|
||||||
|
*
|
||||||
|
* 优点:是一个依赖工厂,它隐藏了对象创建的细节,
|
||||||
|
* 让业务代码只需要关心 “做什么”,而不用关心 “依赖从哪里来”
|
||||||
|
* 所有依赖关系都在di目录下集中配置,一目了然,后续维护时能快速找到所有依赖的创建逻辑。
|
||||||
|
*
|
||||||
|
* 松耦合:
|
||||||
|
* 业务代码(ProductApplicationService)只依赖抽象接口(ProductRepository),
|
||||||
|
* 不依赖具体实现。如果未来需要改用其他数据来源(比如 WebSocket),
|
||||||
|
* 只需修改bindProductRepository的返回值,无需改动业务逻辑。
|
||||||
|
*
|
||||||
|
* 易于测试:
|
||||||
|
* 测试时可以临时替换为模拟实现
|
||||||
|
* productModule.bindProductRepository = () => new MockProductRepository();
|
||||||
|
* 这样测试就可以脱离真实 API,使用预设的模拟数据。
|
||||||
|
*/
|
||||||
|
export const product = {
|
||||||
|
|
||||||
|
// 绑定 仓储抽象接口 到 HTTP具体实现
|
||||||
|
bindProductRepository: (): ProductRepository => {
|
||||||
|
// return new MockProductRepository 直接从真实接口数据改为测试模拟数据
|
||||||
|
return new HttpProductRepository;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 创建应用服务实例
|
||||||
|
createProductApplicationService: (): ProductApplicationService => {
|
||||||
|
return new ProductApplicationService(
|
||||||
|
product.bindProductRepository()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
141
src/domains/product/domain/entities/Product.ts
Normal file
141
src/domains/product/domain/entities/Product.ts
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import { ErrorText } from "../enums/ErrorEnums"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品实体 - 包含商品的核心属性和行为
|
||||||
|
* 在DDD中,实体具有唯一标识和生命周期
|
||||||
|
*/
|
||||||
|
export class Product {
|
||||||
|
private readonly _id: number;
|
||||||
|
private _name: string;
|
||||||
|
private _description: string;
|
||||||
|
private _price: number;
|
||||||
|
private _imageUrl: string;
|
||||||
|
private _stock: number;
|
||||||
|
private _categoryId: number;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
description: string,
|
||||||
|
price: number,
|
||||||
|
imageUrl: string,
|
||||||
|
stock: number,
|
||||||
|
categoryId: number,
|
||||||
|
) {
|
||||||
|
this._id = id;
|
||||||
|
this._name = name;
|
||||||
|
this._description = description;
|
||||||
|
this._price = price;
|
||||||
|
this._imageUrl = imageUrl;
|
||||||
|
this._stock = stock;
|
||||||
|
this._categoryId = categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实体唯一标识
|
||||||
|
get id(): number {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get name(): string {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
public set name(value: string) {
|
||||||
|
this._name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get description(): string {
|
||||||
|
return this._description;
|
||||||
|
}
|
||||||
|
public set description(value: string) {
|
||||||
|
this._description = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public get price(): number {
|
||||||
|
return this._price;
|
||||||
|
}
|
||||||
|
public set price(value: number) {
|
||||||
|
if (value < 0) {
|
||||||
|
throw new Error(ErrorText.PriceNotNegativein);
|
||||||
|
}
|
||||||
|
this._price = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get imageUrl(): string {
|
||||||
|
return this._imageUrl;
|
||||||
|
}
|
||||||
|
public set imageUrl(value: string) {
|
||||||
|
this._imageUrl = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get stock(): number {
|
||||||
|
return this._stock;
|
||||||
|
}
|
||||||
|
public set stock(value: number) {
|
||||||
|
if (value < 0) {
|
||||||
|
throw new Error(ErrorText.StockNotNegativein)
|
||||||
|
}
|
||||||
|
this._stock = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get categoryId(): number {
|
||||||
|
return this._categoryId;
|
||||||
|
}
|
||||||
|
public set categoryId(value: number) {
|
||||||
|
this._categoryId = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查商品是否有库存
|
||||||
|
*/
|
||||||
|
hasStock() {
|
||||||
|
return this._stock > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 减少商品库存
|
||||||
|
* @param quantity 减少的数量
|
||||||
|
*/
|
||||||
|
reduceStock(quantity: number): void {
|
||||||
|
if (quantity <= 0) {
|
||||||
|
throw new Error(ErrorText.StockIsGreaterThanZero)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._stock < quantity) {
|
||||||
|
throw new Error(ErrorText.LowStock)
|
||||||
|
}
|
||||||
|
|
||||||
|
this._stock -= quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class类转化为普通json数据
|
||||||
|
*/
|
||||||
|
toDTO(): Record<string, string | number> {
|
||||||
|
return {
|
||||||
|
id: this._id,
|
||||||
|
name: this._name,
|
||||||
|
description: this._description,
|
||||||
|
price: this._price,
|
||||||
|
imageUrl: this._imageUrl,
|
||||||
|
stock: this._stock,
|
||||||
|
categoryId: this._categoryId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从json数据创建Product类实例
|
||||||
|
* 转化为领域实体 Product
|
||||||
|
*/
|
||||||
|
static fromDTO(data: Record<string, any>): Product {
|
||||||
|
return new Product(
|
||||||
|
data.id,
|
||||||
|
data.name,
|
||||||
|
data.description,
|
||||||
|
data.price,
|
||||||
|
data.imageUrl,
|
||||||
|
data.stock,
|
||||||
|
data.categoryId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/domains/product/domain/enums/ErrorEnums.ts
Normal file
7
src/domains/product/domain/enums/ErrorEnums.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export enum ErrorText {
|
||||||
|
PriceNotNegativein = "商品 价格 不能为负数",
|
||||||
|
StockNotNegativein = "商品 库存 不能为负数",
|
||||||
|
StockIsGreaterThanZero = "减少的库存数量必须大于0",
|
||||||
|
LowStock = "库存不足",
|
||||||
|
IdNotNull = "商品Id不能为空"
|
||||||
|
}
|
||||||
1
src/domains/product/domain/events/README.md
Normal file
1
src/domains/product/domain/events/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# 临时占位,上传空文件夹,保存文件结构用,后面删除
|
||||||
9
src/domains/product/domain/type/ProductType.ts
Normal file
9
src/domains/product/domain/type/ProductType.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export interface ProductType {
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
description: string,
|
||||||
|
price: number,
|
||||||
|
imageUrl: string,
|
||||||
|
stock: number,
|
||||||
|
categoryId: number,
|
||||||
|
}
|
||||||
1
src/domains/product/domain/value-objects/README.md
Normal file
1
src/domains/product/domain/value-objects/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# 临时占位,上传空文件夹,保存文件结构用,后面删除
|
||||||
6
src/domains/product/index.ts
Normal file
6
src/domains/product/index.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export * from './application/services/ProductApplicationService';
|
||||||
|
export * from './domain/entities/Product';
|
||||||
|
export * from './domain/enums/ErrorEnums';
|
||||||
|
export * from './domain/type/ProductType';
|
||||||
|
export * from './infr/api/HttpProductRepository';
|
||||||
|
export * from './ports/repositories/ProductRepository';
|
||||||
27
src/domains/product/infr/api/HttpProductRepository.ts
Normal file
27
src/domains/product/infr/api/HttpProductRepository.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Product } from "@domains/product/domain/entities/Product.ts";
|
||||||
|
import { ProductType } from "@domains/product/domain/type/ProductType.ts";
|
||||||
|
import { ProductRepository } from "@domains/product/ports/repositories/ProductRepository";
|
||||||
|
import { http } from "shared/infra";
|
||||||
|
import HttpConfig from "../config/HttpConfig";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP商品仓储实现 - 实现商品数据的HTTP访问
|
||||||
|
* 这是基础设施层,负责具体的数据获取实现
|
||||||
|
*/
|
||||||
|
export class HttpProductRepository implements ProductRepository {
|
||||||
|
|
||||||
|
async findAll(): Promise<Array<ProductType>> {
|
||||||
|
const result: Array<ProductType> = await http.get({ url: HttpConfig.apiUrlList.productsListUrl });
|
||||||
|
const items = result.map((item) => Product.fromDTO(item))
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findById(id: number): Promise<Product> {
|
||||||
|
const result: ProductType = await http.get({
|
||||||
|
url: HttpConfig.apiUrlList.productsInfoUrl,
|
||||||
|
data: { id }
|
||||||
|
});
|
||||||
|
return Product.fromDTO(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/domains/product/infr/config/HttpConfig.ts
Normal file
11
src/domains/product/infr/config/HttpConfig.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
interface apiUrlListType {
|
||||||
|
productsListUrl: string
|
||||||
|
productsInfoUrl: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class HttpConfig {
|
||||||
|
public static apiUrlList: apiUrlListType = {
|
||||||
|
productsListUrl: '/productsList',
|
||||||
|
productsInfoUrl: '/productsInfo',
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/domains/product/ports/repositories/ProductRepository.ts
Normal file
20
src/domains/product/ports/repositories/ProductRepository.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Product } from "../../domain/entities/Product"
|
||||||
|
import { ProductType } from "../../domain/type/ProductType"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品仓储接口 - 定义商品数据访问的契约
|
||||||
|
* 在DDD中,端口定义了领域层与外部世界的交互方式
|
||||||
|
*/
|
||||||
|
export interface ProductRepository {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取商品列表
|
||||||
|
*/
|
||||||
|
findAll(): Promise<Array<ProductType>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取商品详情
|
||||||
|
* @param id 商品ID
|
||||||
|
*/
|
||||||
|
findById(id: number): Promise<Product>
|
||||||
|
}
|
||||||
1
src/features/product/assets/README.md
Normal file
1
src/features/product/assets/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# 临时占位,上传空文件夹,保存文件结构用,后面删除
|
||||||
1
src/features/product/components/README.md
Normal file
1
src/features/product/components/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# 临时占位,上传空文件夹,保存文件结构用,后面删除
|
||||||
1
src/features/product/composables/README.md
Normal file
1
src/features/product/composables/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# 临时占位,上传空文件夹,保存文件结构用,后面删除
|
||||||
0
src/features/product/index.ts
Normal file
0
src/features/product/index.ts
Normal file
16
src/features/product/router/index.ts
Normal file
16
src/features/product/router/index.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
|
export const productRouter: Array<RouteRecordRaw> = [
|
||||||
|
{
|
||||||
|
path: '/Products',
|
||||||
|
name: 'ProductList',
|
||||||
|
component: () => import('../views/ProductList/ProductList.jsx'),
|
||||||
|
meta: { title: '商品列表' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/ProductDetail',
|
||||||
|
name: 'ProductDetail',
|
||||||
|
component: () => import('../views/ProductDetail/ProductDetail.jsx'),
|
||||||
|
meta: { title: '商品详情' }
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.ProductDetail {
|
||||||
|
.title {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/features/product/views/ProductDetail/ProductDetail.tsx
Normal file
62
src/features/product/views/ProductDetail/ProductDetail.tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import styles from './ProductDetail.module.less'
|
||||||
|
|
||||||
|
import { product } from '@domains/product/di/productModule';
|
||||||
|
import { ProductType } from '@domains/product/domain/type/ProductType';
|
||||||
|
import { defineComponent, onMounted, reactive } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
interface storeType {
|
||||||
|
item: ProductType
|
||||||
|
id: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const route = useRoute();
|
||||||
|
const productService = product.createProductApplicationService();
|
||||||
|
|
||||||
|
const state = reactive<storeType>({
|
||||||
|
item: {} as ProductType,
|
||||||
|
id: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!route.query.id) {
|
||||||
|
throw new Error("缺少id参数");
|
||||||
|
}
|
||||||
|
|
||||||
|
state.id = Number(route.query.id)
|
||||||
|
console.log("id: ", state.id);
|
||||||
|
loadProductDetail()
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadProductDetail = async () => {
|
||||||
|
state.item = await productService.getProductInfo(state.id);
|
||||||
|
console.log("state.item: ", state.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<div class={ styles.ProductDetail }>
|
||||||
|
<h1 class={ styles.title }>商品领域模块 - 商品详情页面</h1>
|
||||||
|
<hr/>
|
||||||
|
<p>商品id:{ state.id }</p>
|
||||||
|
{ state.item.name }
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
Object.entries(state.item).map(([ key, value ]) =>
|
||||||
|
<li>{ key }: { value }</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// export default defineComponent({
|
||||||
|
// setup() {
|
||||||
|
// return () => (
|
||||||
|
// <div>商品详情</div>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// })
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.ProductList {
|
||||||
|
.test {
|
||||||
|
color: var(--theme-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
79
src/features/product/views/ProductList/ProductList.tsx
Normal file
79
src/features/product/views/ProductList/ProductList.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import styles from './ProductList.module.less'
|
||||||
|
|
||||||
|
import { defineComponent, onMounted, reactive } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { product } from '@domains/product/di/productModule';
|
||||||
|
import { ProductType } from '@domains/product/domain/type/ProductType';
|
||||||
|
import { ElCard, ElIcon, Icons } from "shared/element-plus";
|
||||||
|
|
||||||
|
interface storeType {
|
||||||
|
list: Array<ProductType>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const productService = product.createProductApplicationService();
|
||||||
|
|
||||||
|
const state = reactive<storeType>({
|
||||||
|
list: []
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadProductList()
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadProductList = async () => {
|
||||||
|
state.list = await productService.getProductList();
|
||||||
|
console.log("state.list: ", state.list);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ElCardHeader = () => {
|
||||||
|
return (
|
||||||
|
<div class="card-header">
|
||||||
|
<ElIcon size={ 15 }>
|
||||||
|
<Icons.Position></Icons.Position>
|
||||||
|
</ElIcon>
|
||||||
|
<span>Card 头部</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ElCardFooter = () => {
|
||||||
|
return <div>Footer 底部</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<div class={ styles.ProductList }>
|
||||||
|
<h1 class={ styles.test }>商品领域模块 - 商品列表页面3</h1>
|
||||||
|
<hr/>
|
||||||
|
<h2>从Deno 后端 加载的数据,点击进详情</h2>
|
||||||
|
<ElCard>
|
||||||
|
{ {
|
||||||
|
header: () => ElCardHeader(),
|
||||||
|
footer: () => ElCardFooter(),
|
||||||
|
default: () =>
|
||||||
|
state.list.map(item =>
|
||||||
|
<p key={ item.id } onClick={ () => {
|
||||||
|
router.push({ name: 'ProductDetail', query: { id: item.id } });
|
||||||
|
} }>
|
||||||
|
{ item.id } | { item.name } | ¥{ item.price }
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
} }
|
||||||
|
</ElCard>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// export default defineComponent({
|
||||||
|
// setup() {
|
||||||
|
// return () => (
|
||||||
|
// <div>商品列表</div>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// })
|
||||||
12
src/main.ts
Normal file
12
src/main.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// import './assets/main.css'
|
||||||
|
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
import App from './App'
|
||||||
|
|
||||||
|
|
||||||
|
createApp(App)
|
||||||
|
.use(createPinia())
|
||||||
|
.mount('#app')
|
||||||
|
|
||||||
4
src/shared/types/env.d.ts
vendored
Normal file
4
src/shared/types/env.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
declare module '*.module.less' {
|
||||||
|
const classes: { [key: string]: string }
|
||||||
|
export default classes
|
||||||
|
}
|
||||||
29
src/shared/types/shared.d.ts
vendored
Normal file
29
src/shared/types/shared.d.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module 'shared/infra' {
|
||||||
|
export const http: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'shared/utils' {
|
||||||
|
export const UrlCheckUtils: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'shared/element-plus' {
|
||||||
|
import * as ElementPlus from 'element-plus';
|
||||||
|
import * as ElementPlusIcons from '@element-plus/icons-vue';
|
||||||
|
import type { App } from 'vue';
|
||||||
|
export * from 'element-plus';
|
||||||
|
export import Icons = ElementPlusIcons;
|
||||||
|
|
||||||
|
export function init(app: App): void;
|
||||||
|
|
||||||
|
const defaultExport: typeof ElementPlus & {
|
||||||
|
Icons: typeof ElementPlusIcons;
|
||||||
|
init: typeof init;
|
||||||
|
};
|
||||||
|
export default defaultExport;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'shared/normalize' {
|
||||||
|
export const init: () => void;
|
||||||
|
}
|
||||||
33
tsconfig.app.json
Normal file
33
tsconfig.app.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.vue"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"src/**/__tests__/*"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"verbatimModuleSyntax": false,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"jsxImportSource": "vue",
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/app/*"
|
||||||
|
],
|
||||||
|
"@shared/*": [
|
||||||
|
"./src/shared/*"
|
||||||
|
],
|
||||||
|
"@features/*": [
|
||||||
|
"./src/features/*"
|
||||||
|
],
|
||||||
|
"@domains/*": [
|
||||||
|
"./src/domains/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
tsconfig.json
Normal file
11
tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
19
tsconfig.node.json
Normal file
19
tsconfig.node.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": "@tsconfig/node22/tsconfig.json",
|
||||||
|
"include": [
|
||||||
|
"vite.config.*",
|
||||||
|
"vitest.config.*",
|
||||||
|
"cypress.config.*",
|
||||||
|
"nightwatch.conf.*",
|
||||||
|
"playwright.config.*",
|
||||||
|
"eslint.config.*"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true,
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"types": ["node"]
|
||||||
|
}
|
||||||
|
}
|
||||||
66
vite.config.ts
Normal file
66
vite.config.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { defineConfig, loadEnv } from 'vite';
|
||||||
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||||
|
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||||
|
import federation from 'vite-federation';
|
||||||
|
import { fileURLToPath, URL } from "node:url";
|
||||||
|
import { visualizer } from 'rollup-plugin-visualizer';
|
||||||
|
import { reg, init } from 'vite-plugin-nacos';
|
||||||
|
|
||||||
|
export default defineConfig(async ({ mode }) => {
|
||||||
|
const env = loadEnv(mode, process.cwd());
|
||||||
|
const { VITE_MODEL_SHARED_URL } = await init(env)
|
||||||
|
|
||||||
|
// console.log("VITE_MODEL_SHARED_URL: ", VITE_MODEL_SHARED_URL);
|
||||||
|
|
||||||
|
return {
|
||||||
|
plugins: [
|
||||||
|
reg(),
|
||||||
|
|
||||||
|
federation({
|
||||||
|
name: env.VITE_SERVER_NAME,
|
||||||
|
remotes: {
|
||||||
|
shared: `${VITE_MODEL_SHARED_URL}/${env.VITE_MODEL_SHARED_NAME}/assets/remoteEntry.js`
|
||||||
|
},
|
||||||
|
exposes: { // 暴露给主应用的资源(按功能分组)
|
||||||
|
'./ProductDomain': './src/domains/product/index.ts', // 领域层(实体、服务、接口等)
|
||||||
|
'./ProductRouter': './src/features/product/router/index.ts' // 商品路由
|
||||||
|
},
|
||||||
|
shared: {
|
||||||
|
vue: { import: false, generate: false, requiredVersion: '^3.5.18' },
|
||||||
|
'vue-router': { import: false, generate: false, requiredVersion: '^4.0.6' },
|
||||||
|
pinia: { import: false, generate: false, requiredVersion: '^3.0.3' },
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
vue(),
|
||||||
|
vueJsx(),
|
||||||
|
vueDevTools(),
|
||||||
|
visualizer({
|
||||||
|
title: "商品模块依赖分析",
|
||||||
|
filename: 'dist/stats.html', // 分析文件输出路径
|
||||||
|
open: false, // 构建完成后自动打开
|
||||||
|
gzipSize: true, // 显示gzip后的大小
|
||||||
|
brotliSize: true, // 显示brotli后的大小
|
||||||
|
})
|
||||||
|
],
|
||||||
|
build: {
|
||||||
|
target: 'esnext',
|
||||||
|
cssCodeSplit: true,
|
||||||
|
rollupOptions: {
|
||||||
|
// 关键配置:告诉 Rollup 不要处理模块联邦的导入
|
||||||
|
external: ['shared/normalize'],
|
||||||
|
output: {
|
||||||
|
minifyInternalExports: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src/', import.meta.url)),
|
||||||
|
'@shared': fileURLToPath(new URL('./src/shared/', import.meta.url)),
|
||||||
|
'@features': fileURLToPath(new URL('./src/features/', import.meta.url)),
|
||||||
|
'@domains': fileURLToPath(new URL('./src/domains/', import.meta.url))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user