From 007ad2c83117b82284f483ceec47a5bc88626d1f Mon Sep 17 00:00:00 2001 From: yeuimu <2197651308@qq.com> Date: Tue, 1 Sep 2026 16:22:32 +0800 Subject: [PATCH] deploy(v2): h5 build with base /v2/h5, admin dockerfile & nginx route, shared compose --- apps/admin/src/router/index.ts | 2 +- apps/admin/vite.config.ts | 2 +- apps/api/src/public/public.service.spec.ts | 68 ++++++++++++++++++++ apps/api/src/public/public.service.ts | 71 +++++++++++++++----- deploy/admin.Dockerfile | 7 +- deploy/docker-compose.v2.yml | 75 ++++++++++++++++++++++ deploy/h5/index.html | 2 +- deploy/h5/static/js/index.e79d1a25.js | 1 + deploy/nginx/admin.v2.conf | 35 ++++++++++ 9 files changed, 241 insertions(+), 22 deletions(-) create mode 100644 deploy/docker-compose.v2.yml create mode 100644 deploy/h5/static/js/index.e79d1a25.js create mode 100644 deploy/nginx/admin.v2.conf diff --git a/apps/admin/src/router/index.ts b/apps/admin/src/router/index.ts index 5dc7688..1bd4914 100644 --- a/apps/admin/src/router/index.ts +++ b/apps/admin/src/router/index.ts @@ -28,7 +28,7 @@ const routes: RouteRecordRaw[] = [ ] const router = createRouter({ - history: createWebHistory('/admin/'), + history: createWebHistory('/v2/admin/'), routes, }) diff --git a/apps/admin/vite.config.ts b/apps/admin/vite.config.ts index 7b2c2d2..1f6deb4 100644 --- a/apps/admin/vite.config.ts +++ b/apps/admin/vite.config.ts @@ -7,7 +7,7 @@ import { fileURLToPath, URL } from 'node:url' // https://vite.dev/config/ export default defineConfig({ - base: '/admin/', + base: '/v2/admin/', plugins: [ vue(), AutoImport({ diff --git a/apps/api/src/public/public.service.spec.ts b/apps/api/src/public/public.service.spec.ts index c3ce81d..b6be652 100644 --- a/apps/api/src/public/public.service.spec.ts +++ b/apps/api/src/public/public.service.spec.ts @@ -328,6 +328,74 @@ describe('PublicService', () => { expect(result.items[0].goodName).toBe(`Pub High ${stamp}`); // 代表行 = 排序第一条 }); + it('list price is the family price matrix minimum (SQL aggregate)', async () => { + // 自包含 fixture:CUSTOM 光板成员(craftLabel/logisticsLabel 是矩阵归因来源) + // + 单变体 38 元。SDS 成员无标签时矩阵为空(不解析名称,等整理补标签), + // 共享 fixture 族 therefore 无矩阵,无法覆盖该路径。 + const og = await prisma.originGood.create({ + data: { + source: 'CUSTOM', + sdsGoodId: `pub-matrix-${stamp}`, + goodName: `Pub Matrix ${stamp}`, + craftLabel: '不打印', + logisticsLabel: '包邮', + goodPrice: 50, // 无矩阵时的回退链接价 + }, + }); + const family = await prisma.productFamily.create({ + data: { familyName: `Pub Matrix Family ${stamp}`, primaryOriginGoodId: og.id }, + }); + await prisma.originGood.update({ + where: { id: og.id }, + data: { familyId: family.id }, + }); + await prisma.originGoodVariant.create({ + data: { + originGoodId: og.id, + sdsVariantId: `pub-matrix-v-${stamp}`, + sku: `PM${stamp}`, + sizeName: 'S', + price: 38, + }, + }); + const good = await prisma.good.create({ + data: { + goodName: `Pub Matrix Good ${stamp}`, + originGoodId: og.id, + familyId: family.id, + countryId, + categoryId, + }, + }); + try { + // 重算前:族无矩阵 → 回退链接价 + const before = await service.getGoods({ + page: 1, + pageSize: 50, + keyword: `Pub Matrix Good`, + }); + expect(before.items).toHaveLength(1); + expect(before.items[0].price).toBe('50'); + + await new FamilyRecomputeService(prisma).recomputeFamily(family.id); + const after = await service.getGoods({ + page: 1, + pageSize: 50, + keyword: `Pub Matrix Good`, + }); + expect(after.items).toHaveLength(1); + // 重算后:矩阵最低价 38 生效,不再是回退链接价 + expect(after.items[0].price).toBe('38'); + } finally { + await prisma.good.delete({ where: { id: good.id } }); + await prisma.originGoodVariant.deleteMany({ + where: { sdsVariantId: `pub-matrix-v-${stamp}` }, + }); + await prisma.productFamily.delete({ where: { id: family.id } }); + await prisma.originGood.delete({ where: { id: og.id } }); + } + }); + it('custom goods (无族) are not visible on public endpoints', async () => { const customPublicId = `custom-public-${stamp}`; const origin = await prisma.originGood.create({ diff --git a/apps/api/src/public/public.service.ts b/apps/api/src/public/public.service.ts index 0e65289..12883a1 100644 --- a/apps/api/src/public/public.service.ts +++ b/apps/api/src/public/public.service.ts @@ -60,6 +60,24 @@ const PUBLIC_GOOD_INCLUDE = { type PublicGoodRow = Prisma.GoodGetPayload<{ include: typeof PUBLIC_GOOD_INCLUDE }>; +/** + * 列表/首页卡片的精简 include:重 JSON(变体 design_data、detail、merged 副源、 + * 整份族矩阵)全部不进列表查询——全量商品拉这些字段实测要 ~2s,而列表 DTO + * 一个都不用。详情接口仍走 PUBLIC_GOOD_INCLUDE。 + */ +const PUBLIC_GOOD_LIST_INCLUDE = { + country: true, + category: true, + tag: { include: { tagGroup: true } }, + position: true, + originGood: { + select: { sdsGoodId: true, goodImage: true, goodPrice: true }, + }, + goodTags: { include: { tag: { include: { tagGroup: true } } } }, +} satisfies Prisma.GoodInclude; + +type PublicGoodListRow = Prisma.GoodGetPayload<{ include: typeof PUBLIC_GOOD_LIST_INCLUDE }>; + @Injectable() export class PublicService { constructor(private readonly prisma: PrismaService) {} @@ -214,10 +232,11 @@ export class PublicService { // 再内存分组、分页作用于分组结果 —— 若量级上万需改为物化族表查询。 const rows = await this.prisma.good.findMany({ where, - include: PUBLIC_GOOD_INCLUDE, + include: PUBLIC_GOOD_LIST_INCLUDE, orderBy, }); - const grouped = new Map(); + const familyMinPrices = await this.loadFamilyMinPrices(); + const grouped = new Map(); for (const good of rows) { const key = good.familyId ? `f:${good.familyId}` : `g:${good.id}`; const bucket = grouped.get(key); @@ -228,8 +247,10 @@ export class PublicService { const rep = goods[0]; const dto = this.toPublicGood(rep); // 列表价 = 族矩阵最低价("这个款之下有哪些价格"的起价);无矩阵回退链接价 - const familyMin = this.familyMinPrice(rep); - if (rep.familyId && familyMin !== null) dto.price = familyMin; + const familyMin = rep.familyId + ? familyMinPrices.get(rep.familyId.toString()) + : undefined; + if (familyMin !== undefined) dto.price = familyMin; return dto; }); if (query.sort === 'PRICE_ASC' || query.sort === 'PRICE_DESC') { @@ -249,14 +270,28 @@ export class PublicService { }; } - /** 族物化矩阵的最低价;无族/无矩阵返回 null */ - private familyMinPrice(good: PublicGoodRow): string | null { - const matrix = good.originGood.family?.priceMatrix as - | { rows?: Array<{ price: string }> } - | null - | undefined; - const prices = (matrix?.rows ?? []).map((r) => Number(r.price)).filter((n) => Number.isFinite(n)); - return prices.length ? String(Math.min(...prices)) : null; + /** + * 族最低价一次 SQL 聚合:price_matrix 是每族 ~11KB 的 JSONB,按行 include + * 会让每个商品都携带整份矩阵(实测全量 ~330ms);PG 端展开聚合只回传 + * 每族一个数字。非数字/缺失 price 的行跳过,与旧内存版过滤语义一致。 + */ + private async loadFamilyMinPrices(): Promise> { + const rows = await this.prisma.$queryRaw< + Array<{ family_id: bigint | string; min_price: Prisma.Decimal | null }> + >` + SELECT f.family_id, MIN((r->>'price')::numeric) AS min_price + FROM product_families f + CROSS JOIN LATERAL jsonb_array_elements(f.price_matrix->'rows') AS r + WHERE (r->>'price') ~ '^-?[0-9]+(\.[0-9]+)?$' + GROUP BY f.family_id + `; + const result = new Map(); + for (const row of rows) { + if (row.min_price !== null) { + result.set(row.family_id.toString(), String(Number(row.min_price))); + } + } + return result; } /** @@ -321,7 +356,7 @@ export class PublicService { originGood: { delisted: false }, ...(query.countryId ? { countryId: BigInt(query.countryId) } : {}), }, - include: PUBLIC_GOOD_INCLUDE, + include: PUBLIC_GOOD_LIST_INCLUDE, orderBy: [ { position: { indexVal: 'asc' } }, { goodPriority: 'desc' }, @@ -329,6 +364,7 @@ export class PublicService { ], take: query.limit, }); + const familyMinPrices = await this.loadFamilyMinPrices(); // 首页同样按族去重(同族多条位置配置只保留排序最前一条),再截取 limit const seen = new Set(); const items: PublicGoodDto[] = []; @@ -337,14 +373,17 @@ export class PublicService { if (seen.has(key)) continue; seen.add(key); const dto = this.toPublicGood(good); - const familyMin = this.familyMinPrice(good); - if (good.familyId && familyMin !== null) dto.price = familyMin; + const familyMin = good.familyId + ? familyMinPrices.get(good.familyId.toString()) + : undefined; + if (familyMin !== undefined) dto.price = familyMin; items.push(dto); } return items.slice(0, query.limit); } - private toPublicGood(good: PublicGoodRow): PublicGoodDto { + /** 入参用精简行类型:列表(PublicGoodListRow)与详情(PublicGoodRow,字段超集)都能传 */ + private toPublicGood(good: PublicGoodListRow): PublicGoodDto { const formatGroup = (group: { id: bigint; groupName: string; sortOrder: number } | null) => group ? { id: group.id.toString(), groupName: group.groupName, sortOrder: group.sortOrder } diff --git a/deploy/admin.Dockerfile b/deploy/admin.Dockerfile index b73ace4..2e25a75 100644 --- a/deploy/admin.Dockerfile +++ b/deploy/admin.Dockerfile @@ -1,4 +1,5 @@ -# InkReach Admin - Vite build served by nginx +# InkReach Admin (v2) - Vite build served by nginx +# v2 部署本地化:与现网共用 official.inkreach.cc,SPA 路径挂 /v2/admin/ 下 FROM node:20-alpine AS build WORKDIR /app RUN corepack enable @@ -16,7 +17,7 @@ COPY apps/admin apps/admin RUN pnpm --filter @inkreach/admin exec vite build FROM nginx:1.27-alpine -COPY --from=build /app/apps/admin/dist /usr/share/nginx/html/admin +COPY --from=build /app/apps/admin/dist /usr/share/nginx/html/v2/admin COPY deploy/nginx/admin.conf /etc/nginx/conf.d/default.conf -COPY deploy/h5 /usr/share/nginx/html/h5 +COPY deploy/h5 /usr/share/nginx/html/v2/h5 EXPOSE 80 diff --git a/deploy/docker-compose.v2.yml b/deploy/docker-compose.v2.yml new file mode 100644 index 0000000..1bf6453 --- /dev/null +++ b/deploy/docker-compose.v2.yml @@ -0,0 +1,75 @@ +# v2 部署(refactor/v2 分支):与现网 deploy 项目完全隔离(独立库/独立卷/独立网络), +# 对外复用 official.inkreach.cc 域名,由现网 nginx 按路径分流: +# https://official.inkreach.cc/v2/admin/ -> 本栈 admin(SPA) +# https://official.inkreach.cc/v2-api/ -> 本栈 api(去掉前缀后转发) +# 本栈两容器挂到外部网络 inkreach-shared,与 deploy-admin-1 互通(别名 v2-api / v2-admin)。 +name: deploy-v2 + +services: + postgres: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_USER: inkreach + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: inkreach + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U inkreach -d inkreach"] + interval: 10s + timeout: 5s + retries: 10 + networks: + - default + + api: + build: + context: .. + dockerfile: deploy/api.Dockerfile + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + NODE_ENV: production + PORT: 3001 + DATABASE_URL: postgresql://inkreach:${POSTGRES_PASSWORD}@postgres:5432/inkreach + JWT_SECRET: ${JWT_SECRET} + CORS_ORIGINS: "*" + # 激活 product-family 聚合公开读路径(现网 v1 未设置,保持旧行为,便于对比) + PUBLIC_DETAIL_FROM_FAMILY: "true" + volumes: + - uploads:/app/uploads + networks: + default: + shared: + aliases: + - v2-api + + admin: + build: + context: .. + dockerfile: deploy/admin.Dockerfile + args: + VITE_API_BASE: /v2-api/ + restart: unless-stopped + depends_on: + - api + volumes: + - ./nginx/admin.v2.conf:/etc/nginx/conf.d/default.conf:ro + networks: + default: + shared: + aliases: + - v2-admin + +networks: + default: + shared: + external: true + name: inkreach-shared + +volumes: + pgdata: + uploads: \ No newline at end of file diff --git a/deploy/h5/index.html b/deploy/h5/index.html index 82a27b6..c129930 100644 --- a/deploy/h5/index.html +++ b/deploy/h5/index.html @@ -1,2 +1,2 @@ 印美达POD供应链
\ No newline at end of file + document.write('')
\ No newline at end of file diff --git a/deploy/h5/static/js/index.e79d1a25.js b/deploy/h5/static/js/index.e79d1a25.js new file mode 100644 index 0000000..ba6ecaa --- /dev/null +++ b/deploy/h5/static/js/index.e79d1a25.js @@ -0,0 +1 @@ +(function(e){function t(t){for(var o,i,c=t[0],u=t[1],d=t[2],s=0,f=[];s>>/g,">"),e=e.split(">").map((function(e){return e.trim().split(" ").join("').descendant('")})).join("').child('"),function(t,n,o){return new t(n,o).descendant(e)}},d=function(){function e(t){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];(0,r.default)(this,e),this.nodes=[t],this.all=n}return(0,i.default)(e,[{key:"child",value:function(e){var t=[];if(this.all)this.nodes.forEach((function(n){var o;(o=t).push.apply(o,(0,a.default)(n.$children.filter((function(t){return c(t,e)}))))}));else if(this.nodes.length>0){var n=this.nodes[0].$children.find((function(t){return c(t,e)}));t=n?[n]:[]}return this.nodes=t,this}},{key:"descendant",value:function(e){var t=this,n=[];return this.nodes.forEach((function(o){(function(){var e=!1;return function t(n,o){if(!e&&"function"===typeof o)for(var a=n.$children,r=0;!e&&r",nbsp:" ",amp:"&",quot:'"'};return e.replace(/&(lt|gt|nbsp|amp|quot);/gi,(function(e,n){return t[n]}))},t.html2Escape=function(e){return e?e.replace(/[<>&"]/g,(function(e){return{"<":"<",">":">","&":"&",'"':"""}[e]})):e},n("5c47"),n("a1c1")},4045:function(e,t,n){"use strict";n("6a54"),Object.defineProperty(t,"__esModule",{value:!0}),t.getRelationNodes=function(e){if(!this.$unicom)throw"this.getRelationNodes()需与p-f-unicom配合使用!";return this.$unicom("@"+e)}},"5da8":function(e,t,n){"use strict";n.d(t,"b",(function(){return o})),n.d(t,"c",(function(){return a})),n.d(t,"a",(function(){}));var o=function(){var e=this.$createElement,t=this._self._c||e;return t("App",{attrs:{keepAliveInclude:this.keepAliveInclude}})},a=[]},"792a":function(e,t,n){"use strict";var o=n("9d8b"),a=n.n(o);a.a},"7a41":function(e,t,n){"use strict";n("6a54"),Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,n("5c47"),n("2c10"),n("c9b5"),n("bf0f"),n("ab80"),n("473f"),n("f7a5");var o=function(e,t,n){return Object(e)!==e||(Array.isArray(t)||(t=t.toString().match(/[^.[\]]+/g)||[]),t.slice(0,-1).reduce((function(e,n,o){return Object(e[n])===e[n]?e[n]:e[n]=Math.abs(t[o+1])>>0===+t[o+1]?[]:{}}),e)[t[t.length-1]]=n),e};t.default=o},"95d5":function(e,t,n){var o=n("c86c");t=o(!1),t.push([e.i,"/* 全局样式 - 印美达 Inkreach 微信小程序 */uni-page-body{--color-primary:#ff6800;--color-primary-light:#fff0e5;--color-primary-dark:#e55a00;--color-secondary:#f26522;--color-dark:#121212;--color-text-primary:#333;--color-text-secondary:#666;--color-text-tertiary:#999;--color-bg-page:#f5f5f5;--color-bg-card:#fff;--color-border:#eee;--color-tag-bg:#f2f2f2;--color-success:#52c41a;--color-warning:#faad14;--radius-sm:%?8?%;--radius-md:%?12?%;--radius-lg:%?20?%;--radius-xl:%?32?%;--spacing-xs:%?8?%;--spacing-sm:%?16?%;--spacing-md:%?24?%;--spacing-lg:%?32?%;--spacing-xl:%?48?%;font-size:%?28?%;color:var(--color-text-primary);background-color:var(--color-bg-page);font-family:CusPingFangSC,-apple-system,BlinkMacSystemFont,PingFang SC,Helvetica Neue,sans-serif}body.?%PAGE?%{background-color:var(--color-bg-page)}\n/* 通用样式 */.container{min-height:100vh;background-color:var(--color-bg-page)}.flex-row{display:flex;flex-direction:row;align-items:center}.flex-col{display:flex;flex-direction:column}.flex-center{display:flex;align-items:center;justify-content:center}.flex-between{display:flex;align-items:center;justify-content:space-between}.flex-1{flex:1}.flex-wrap{display:flex;flex-wrap:wrap}\n/* 文字样式 */.text-primary{color:var(--color-primary)}.text-secondary{color:var(--color-text-secondary)}.text-tertiary{color:var(--color-text-tertiary)}.text-white{color:#fff}.text-bold{font-weight:600}.text-center{text-align:center}.text-sm{font-size:%?24?%}.text-md{font-size:%?28?%}.text-lg{font-size:%?32?%}.text-xl{font-size:%?40?%}.text-xxl{font-size:%?56?%}\n/* 间距 */.mt-xs{margin-top:%?8?%}.mt-sm{margin-top:%?16?%}.mt-md{margin-top:%?24?%}.mt-lg{margin-top:%?32?%}.mt-xl{margin-top:%?48?%}.mb-xs{margin-bottom:%?8?%}.mb-sm{margin-bottom:%?16?%}.mb-md{margin-bottom:%?24?%}.mb-lg{margin-bottom:%?32?%}.p-sm{padding:%?16?%}.p-md{padding:%?24?%}.p-lg{padding:%?32?%}.px-md{padding-left:%?24?%;padding-right:%?24?%}.px-lg{padding-left:%?32?%;padding-right:%?32?%}\n/* 按钮 */.btn-primary{display:inline-flex;align-items:center;justify-content:center;background-color:var(--color-primary);color:#fff;font-size:%?28?%;font-weight:500;padding:%?20?% %?48?%;border-radius:var(--radius-lg);border:none;line-height:1.5}.btn-primary::after{border:none}.btn-outline{display:inline-flex;align-items:center;justify-content:center;background-color:initial;color:var(--color-primary);font-size:%?28?%;font-weight:500;padding:%?20?% %?48?%;border-radius:var(--radius-lg);border:%?2?% solid var(--color-primary);line-height:1.5}.btn-outline::after{border:none}\n/* 卡片 */.card{background-color:var(--color-bg-card);border-radius:var(--radius-md);padding:%?24?%;box-shadow:0 %?2?% %?12?% rgba(0,0,0,.04)}\n/* 标签 */.tag{display:inline-flex;align-items:center;padding:%?4?% %?16?%;border-radius:var(--radius-sm);font-size:%?20?%;background-color:var(--color-tag-bg);color:var(--color-text-secondary)}.tag-primary{background-color:var(--color-primary-light);color:var(--color-primary)}\n/* 分割线 */.divider{width:100%;height:%?1?%;background-color:var(--color-border)}\n/* 安全区底部 */.safe-bottom{padding-bottom:env(safe-area-inset-bottom)}\n/* 全局隐藏横向滚动条(scroll-view / webkit) */::-webkit-scrollbar{display:none;width:0;height:0;background:transparent}",""]),e.exports=t},"98bce":function(e,t,n){"use strict";n.r(t);var o=n("efa3"),a=n.n(o);for(var r in o)["default"].indexOf(r)<0&&function(e){n.d(t,e,(function(){return o[e]}))}(r);t["default"]=a.a},"9d8b":function(e,t,n){var o=n("95d5");o.__esModule&&(o=o.default),"string"===typeof o&&(o=[[e.i,o,""]]),o.locals&&(e.exports=o.locals);var a=n("967d").default;a("37b6588b",o,!0,{sourceMap:!1,shadowMode:!1})},ac9f:function(e,t,n){"use strict";n("6a54"),Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var o=null,a=function(e,t,n){null!==o&&clearTimeout(o),o=setTimeout((function(){e[t]()}),n)};t.default=a},bad1:function(e,t,n){"use strict";(function(e){var t=n("f5bd").default;n("473f"),n("bf0f"),n("de6c"),n("5c47"),n("a1c1");var o=t(n("9b8e")),a={keys:function(){return[]}};e["____73E5929____"]=!0,delete e["____73E5929____"],e.__uniConfig={tabBar:{color:"#999999",selectedColor:"#FF6800",backgroundColor:"#ffffff",borderStyle:"white",list:[{pagePath:"pages/index/index",text:"首页",iconPath:"static/icons/tabbar/tab_home_gray.png",selectedIconPath:"static/icons/tabbar/tab_home_orange.png",redDot:!1,badge:""},{pagePath:"pages/products/products",text:"产品",iconPath:"static/icons/tabbar/tab_product_gray.png",selectedIconPath:"static/icons/tabbar/tab_product_orange.png",redDot:!1,badge:""}]},style:"v2",sitemapLocation:"sitemap.json",lazyCodeLoading:"requiredComponents",globalStyle:{navigationBarBackgroundColor:"#ffffff",navigationBarTextStyle:"black",navigationBarTitleText:"印美达 Inkreach",backgroundColor:"#f5f5f5",backgroundTextStyle:"dark",enablePullDownRefresh:!1}},e.__uniConfig.compilerVersion="5.24",e.__uniConfig.darkmode=!1,e.__uniConfig.themeConfig={},e.__uniConfig.uniPlatform="h5",e.__uniConfig.appId="__UNI__73E5929",e.__uniConfig.appName="inkreach-miniprogram",e.__uniConfig.appVersion="1.0.0",e.__uniConfig.appVersionCode="100",e.__uniConfig.router={mode:"history",base:"/v2/h5/"},e.__uniConfig.publicPath="/v2/h5/",e.__uniConfig["async"]={loading:"AsyncLoading",error:"AsyncError",delay:200,timeout:6e4},e.__uniConfig.debug=!1,e.__uniConfig.networkTimeout={request:6e4,connectSocket:6e4,uploadFile:6e4,downloadFile:6e4},e.__uniConfig.sdkConfigs={},e.__uniConfig.qqMapKey=void 0,e.__uniConfig.googleMapKey=void 0,e.__uniConfig.aMapKey=void 0,e.__uniConfig.aMapSecurityJsCode=void 0,e.__uniConfig.aMapServiceHost=void 0,e.__uniConfig.locale="",e.__uniConfig.fallbackLocale=void 0,e.__uniConfig.locales=a.keys().reduce((function(e,t){var n=t.replace(/\.\/(uni-app.)?(.*).json/,"$2"),o=a(t);return Object.assign(e[n]||(e[n]={}),o.common||o),e}),{}),e.__uniConfig.nvue={"flex-direction":"column"},e.__uniConfig.__webpack_chunk_load__=n.e,o.default.component("pages-index-index",(function(e){var t={component:n.e("pages-index-index").then(function(){return e(n("3dd4"))}.bind(null,n)).catch(n.oe),delay:__uniConfig["async"].delay,timeout:__uniConfig["async"].timeout};return __uniConfig["async"]["loading"]&&(t.loading={name:"SystemAsyncLoading",render:function(e){return e(__uniConfig["async"]["loading"])}}),__uniConfig["async"]["error"]&&(t.error={name:"SystemAsyncError",render:function(e){return e(__uniConfig["async"]["error"])}}),t})),o.default.component("pages-products-products",(function(e){var t={component:n.e("pages-products-products").then(function(){return e(n("379b"))}.bind(null,n)).catch(n.oe),delay:__uniConfig["async"].delay,timeout:__uniConfig["async"].timeout};return __uniConfig["async"]["loading"]&&(t.loading={name:"SystemAsyncLoading",render:function(e){return e(__uniConfig["async"]["loading"])}}),__uniConfig["async"]["error"]&&(t.error={name:"SystemAsyncError",render:function(e){return e(__uniConfig["async"]["error"])}}),t})),o.default.component("pages-product-detail-product-detail",(function(e){var t={component:n.e("pages-product-detail-product-detail").then(function(){return e(n("0292"))}.bind(null,n)).catch(n.oe),delay:__uniConfig["async"].delay,timeout:__uniConfig["async"].timeout};return __uniConfig["async"]["loading"]&&(t.loading={name:"SystemAsyncLoading",render:function(e){return e(__uniConfig["async"]["loading"])}}),__uniConfig["async"]["error"]&&(t.error={name:"SystemAsyncError",render:function(e){return e(__uniConfig["async"]["error"])}}),t})),e.__uniRoutes=[{path:"/",alias:"/pages/index/index",component:{render:function(e){return e("Page",{props:Object.assign({isQuit:!0,isEntry:!0,isTabBar:!0,tabBarIndex:0},__uniConfig.globalStyle,{navigationBarTitleText:"印美达 Inkreach",navigationStyle:"custom",usingComponents:{}})},[e("pages-index-index",{slot:"page"})])}},meta:{id:1,name:"pages-index-index",isNVue:!1,maxWidth:0,pagePath:"pages/index/index",isQuit:!0,isEntry:!0,isTabBar:!0,tabBarIndex:0,windowTop:0}},{path:"/pages/products/products",component:{render:function(e){return e("Page",{props:Object.assign({isQuit:!0,isTabBar:!0,tabBarIndex:1},__uniConfig.globalStyle,{navigationBarTitleText:"产品中心",navigationStyle:"custom",usingComponents:{}})},[e("pages-products-products",{slot:"page"})])}},meta:{id:2,name:"pages-products-products",isNVue:!1,maxWidth:0,pagePath:"pages/products/products",isQuit:!0,isTabBar:!0,tabBarIndex:1,windowTop:0}},{path:"/pages/product-detail/product-detail",component:{render:function(e){return e("Page",{props:Object.assign({},__uniConfig.globalStyle,{navigationBarTitleText:"产品详情",navigationStyle:"custom",usingComponents:{}})},[e("pages-product-detail-product-detail",{slot:"page"})])}},meta:{name:"pages-product-detail-product-detail",isNVue:!1,maxWidth:0,pagePath:"pages/product-detail/product-detail",windowTop:0}},{path:"/choose-location",component:{render:function(e){return e("Page",{props:{navigationStyle:"custom"}},[e("system-choose-location",{slot:"page"})])}},meta:{name:"choose-location",pagePath:"/choose-location"}},{path:"/open-location",component:{render:function(e){return e("Page",{props:{navigationStyle:"custom"}},[e("system-open-location",{slot:"page"})])}},meta:{name:"open-location",pagePath:"/open-location"}}],e.UniApp&&new e.UniApp}).call(this,n("0ee4"))},beb3:function(e,t,n){"use strict";(function(e){n("6a54"),Object.defineProperty(t,"__esModule",{value:!0}),t.getTabBar=function(){return{setData:function(t){var n,o,a,r;"function"===typeof(null===(n=this.$mp)||void 0===n||null===(o=n.page)||void 0===o?void 0:o.getTabBar)&&null!==(a=this.$mp)&&void 0!==a&&null!==(r=a.page)&&void 0!==r&&r.getTabBar()?this.$mp.page.getTabBar().setData(t):e.log("当前平台不支持getTabBar(),已稍作处理,详细请参见相关文档。")}}}}).call(this,n("ba7c")["default"])},c31e:function(e,t,n){"use strict";n("6a54"),Object.defineProperty(t,"__esModule",{value:!0}),t.parseEventDynamicCode=function(e,t){"function"===typeof this[t]&&this[t](e)}},cb0d:function(e,t,n){"use strict";var o=n("f5bd").default;Object.defineProperty(t,"__esModule",{value:!0}),t.setData=function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;Object.keys(e).forEach((function(n){if((0,a.default)(t,n,e[n]),r.test(n)&&n.endsWith("Clone")){var o=n.replace(/Clone$/,"");t.$options&&t.$options.propsData[o]&&t.$emit("update:".concat(o),e[n])}})),this.$forceUpdate(),"function"==typeof n&&this.$nextTick(n)},n("bf0f"),n("2797"),n("5c47"),n("0506"),n("dc8a"),n("a1c1"),n("20f3"),n("f7a5"),n("6a54"),n("9327");var a=o(n("7a41"));o(n("ac9f"));var r=/^([^\x00-\xff]|[a-zA-Z_$])([^\x00-\xff]|[a-zA-Z0-9_$])*$/},d575:function(e,t,n){"use strict";n("6a54"),Object.defineProperty(t,"__esModule",{value:!0}),t.handleDataset=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};e&&!e.currentTarget&&(t.tagId?e.currentTarget={id:t.tagId}:e.currentTarget={dataset:t})}},dc38:function(e,t,n){"use strict";n("6a54");var o=n("f5bd").default;Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=o(n("9b1b")),r=n("ff7a"),i=n("00f0"),c=n("d575"),u=n("3f573"),d=n("c31e"),l=n("beb3"),s=n("4045"),f=n("372e"),p=n("cb0d"),g={install:function(e,t){e.mixin((0,a.default)((0,a.default)({},r.pageLifetimes),{},{methods:{clone:i.clone,handleDataset:c.handleDataset,escape2Html:u.escape2Html,html2Escape:u.html2Escape,parseEventDynamicCode:d.parseEventDynamicCode,getTabBar:l.getTabBar,getRelationNodes:s.getRelationNodes,zpSelectComponent:f.selectComponent,zpSelectAllComponents:f.selectAllComponents,setData:p.setData}}))}};t.default=g},e4af:function(e,t,n){"use strict";var o=n("f5bd").default,a=o(n("9b1b"));n("3dde"),n("a8b2"),n("1480"),n("6e4a"),n("bad1"),n("d6b2");var r=o(n("3c6c")),i=o(n("dc38")),c=o(n("9b8e"));c.default.use(i.default),c.default.config.productionTip=!1,r.default.mpType="app";var u=new c.default((0,a.default)({},r.default));u.$mount()},efa3:function(e,t,n){"use strict";n("6a54"),Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var o={data:function(){return{}},globalData:{userInfo:null,baseUrl:"https://api.inkreach.com"},onLaunch:function(){var e=uni.getWindowInfo();this.globalData.statusBarHeight=e.statusBarHeight,this.globalData.windowWidth=e.windowWidth,this.globalData.windowHeight=e.windowHeight}};t.default=o},ff7a:function(e,t,n){"use strict";function o(e,t){e.$children.map((function(e){"function"==typeof e[t]&&e[t](),o(e,t)}))}n("6a54"),Object.defineProperty(t,"__esModule",{value:!0}),t.pageLifetimes=void 0,n("fd3c");var a={onLoad:function(){var e=this;uni.onWindowResize((function(t){o(e,"handlePageResize")}))},onShow:function(){o(this,"handlePageShow")},onHide:function(){o(this,"handlePageHide")},onResize:function(){}};t.pageLifetimes=a}}); \ No newline at end of file diff --git a/deploy/nginx/admin.v2.conf b/deploy/nginx/admin.v2.conf new file mode 100644 index 0000000..cc68c0a --- /dev/null +++ b/deploy/nginx/admin.v2.conf @@ -0,0 +1,35 @@ +# v2 部署本地化:这个 nginx 容器只负责把 /v2/admin/ 下的 SPA 静态文件吐出去 +# 对外分流由现网 deploy-admin-1 的 nginx 完成(/v2/admin/ 原样透传、/v2-api/ 转发到 v2 api) +server { + listen 80; + server_name _; + + client_max_body_size 10m; + + root /usr/share/nginx/html; + + location = /v2 { + return 301 /v2/admin/; + } + + location /v2/admin/ { + try_files $uri $uri/ /v2/admin/index.html; + } + + # v2 版 H5(构建时 router.base 必须为 /v2/h5/,产物放 /usr/share/nginx/html/v2/h5) + location = /v2/h5 { + return 301 /v2/h5/; + } + + location = /v2/h5/index.html { + add_header Cache-Control "no-cache"; + } + + location /v2/h5/ { + try_files $uri $uri/ /v2/h5/index.html; + } + + location / { + return 404; + } +} \ No newline at end of file