Files
guilixu-admin/.workbuddy/memory/2026-08-17.md
T
gxwebsoft fe97f49afe fix(shop): 修复商城商品导出及专区商品分页问题
- 修复专区商品抽屉分页中20/100条/页按键无效,增加动态分页参数支持
- 修复商城商品导出功能无反应问题,调整loading初始值保证导出流程执行
- 支持商品导出携带当前筛选条件,保证导出数据符合筛选结果
- 商品导出增加封面图路径列,导出URL文本方式避免跨域限制
- 优化UserSelectModal手机号码展示,改为优先展示真实号码字段
- 相关组件调整分页及导出事件参数,提升交互准确性与稳定性
2026-08-17 12:09:38 +08:00

182 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-08-17 工作日志
## 专区商品「移除」假成功 bug 修复
- 现象:guilixu.websoft.top/special/zone 移除专区商品提示成功,刷新(重查库)后商品仍在。
- 根因:`ShopHomeSectionController.removeGoods``listByIds` + `updateBatchById``shop_goods.section_ids`;单专区商品移除后 section_ids 变 null,但 `ShopGoods.sectionIds` 无更新策略注解,MP 全局默认 `NOT_NULL` 会忽略 null 值 → 不生成 `SET section_ids=NULL` → 库字段保持原值(假成功)。多专区商品(变非 null)正常,故仅单专区场景坏。
- 修复:`addGoods`/`removeGoods` 改用 `UpdateWrapper.set("section_ids", newVal).eq("goods_id", id)` 显式更新(newVal 可为 null),绕过字段策略。文件:guilixu-java `ShopHomeSectionController.java`
- 部署:需重新编译部署 guilixu-java(线上 shop-api)后端生效;前端无需改动。添加用同样模式统一修复。
- 验证:部署后用单专区商品移除测试,刷新不应再出现。
- 注:本机环境无 maven,未本地编译;改动为标准 MP 用法(IService.update(Wrapper) / UpdateWrapper.set 允许 null)。
## 专区白名单功能诊断(功能当前不可用)
- 设计意图:专区设为"受限专区"(restricted=1) 后,该专区商品仅白名单用户可用余额购买;小程序结算页 checkout.tsx 已调 `check-permission` 前端拦截。
- 致命错配:后台白名单弹窗 `UserSelectModal.vue``pageUsers`(@/api/system/user → sys_user 后台员工) 选用户;后端 `ShopHomeSectionController.checkPermission``getLoginUser().getUserId()`= shop_user 小程序买家 userId)。两套用户体系 id 不一致 → 白名单里加的任何人(后台员工)对真实买家都匹配不上 → 受限专区对买家永远拦截/强制余额,白名单形同虚设。
- 次要隐患:后端创建订单接口未做白名单强校验,仅前端拦截,可被绕过。
- 修复方向:① 白名单弹窗数据源从 `pageUsers`(sys_user) 改为 `pageShopUser`(@/api/shop/shopUser → shop_user 买家),使存的 user_id 与校验 userId 同体系;② 后端下单接口加白名单强校验防绕过。后台已有 pageShopUser 可用。
## 专区白名单:用户体系澄清 + 后端强校验(用户 2026-08-17 01:50 指令)
- 用户澄清:本项目的买家(小程序用户)就是 `sys_user` 体系,`pageShopUser` 实际不用/返回空。故白名单弹窗用 `pageUsers`(sys_user) 是**正确**的,之前"用户体系错配"判断为误判。
- 白名单弹窗 `UserSelectModal.vue` 现状(8-16 已改):`pageUsers({ keywords, ... })`**不传 isStaff**(搜索覆盖全部 sys_user)。源码无需再改。已 grep 确认 zone 目录下无残留 isStaff/pageShopUser。
- 后端补「受限专区白名单强校验」:`OrderBusinessService.java` 新增
- import`ShopHomeSectionService``ShopHomeSectionUserService``QueryWrapper``java.util.Set/LinkedHashSet`
- `@Resource` 注入 `homeSectionService``homeSectionUserService`
- `createOrder``validateDeliveryRegionIfNeeded` 之后调用 `validateSectionPermissionIfNeeded(request, shopOrder, loginUser)`
- 新方法逻辑:遍历 `request.goodsItems` → 取 `ShopGoods.sectionIds` → 找 restricted=1 的专区 → 校验 `loginUser.getUserId()` 是否在 `shop_home_section_user`count>0),否则抛 `BusinessException("该商品属于受限专区,仅限指定白名单用户购买")`
- 作用:前端 `check-permission` 的后端兜底,防止直接调 `/shop/shop-order` 下单绕过白名单。
- 部署:需重新编译部署 guilixu-javashop-api)。前端无需改动。
- 注:本机无 maven,未本地编译;改动为标准 MP 用法(IService.count(QueryWrapper) / getById)。
- 待确认:用户 "2.帮我补" 指令只写了前半句,已按上文补后端强校验;若其本意是"补菜单记录"(专区管理入口,动态路由待插入),需另出 INSERT SQL(菜单表结构待确认)。
## 专区白名单弹窗 checkbox 不显示(第三次修复,根因确认)
- 现象:白名单弹窗数据正常(12 条用户),但表格无 checkbox 勾选列,用户无法选择。
- 已尝试修复(前两次均未生效):
1. 8-16`reactive``computed` + `columnWidth: 48`(与 shopCoupon 对齐)
2. 同期:去掉 `isStaff: true`
- **真正根因**(读 `ele-pro-table` 源码 `node_modules/ele-admin-pro/es/ele-pro-table/index.js` 第 94-114 行确认):
- `ele-pro-table` 内部 `tableSelectionType` computed 有前置条件:必须传 `selection``current``selectionType="radio"` 三者之一,否则返回 `undefined`
- 返回 undefined → `tableRowSelection` 也返回 undefined → ant-design-vue Table 收到 `rowSelection=undefined`**不渲染 checkbox 列**
- 我们的弹窗三个条件都不满足(没传 :selection / :current / selectionType),所以不管 row-selection 配置多正确都不会出勾选框
- **最终修复**:模板 `<ele-pro-table>``selection-type="checkbox"`(第 18 行),使源码判断走通:`props2.selectionType !== "radio"` 为 true(是 "checkbox")→ 不走 early return → 返回 "checkbox" → checkbox 列正常渲染。
- 改动文件:`src/views/special/zone/components/UserSelectModal.vue`(仅模板加 1 个 prop
- 部署:重新构建前端即可(`npm run dev` 热更新或 `npm run build` 部署)。
## 专区白名单弹窗 checkbox 修复(8-17 纠正:8-16 修复实际无效)
- 反馈:用户截图仍无勾选框,8-16 加的 `selection-type="checkbox"` 未生效。
- **重读 ele-pro-table 源码确认 8-16 根因判断错误**(`node_modules/ele-admin-pro/es/ele-pro-table/index.js` 94-103 行):
```js
const noSelection = typeof props2.selection === "undefined";
const noCurrent = typeof props2.current === "undefined";
if (noSelection && noCurrent && props2.selectionType !== "radio") {
return; // 不渲染
}
```
三个条件**同时满足**才 return。即 `noSelection && noCurrent && selectionType !== "radio"`。我传的 `selection-type="checkbox"` 让 `selectionType !== "radio"` 为 true,反而**满足**了 early return 条件 → 不渲染。这正是 8-16 修复无效的真因。
- 另外发现 `tableRowSelection.selectedRowKeys` 强制用内部 ref(源码 111 行),**不支持外部初始化已选**——即使绕过 selection 限制,弹窗打开时已选的人也不会显示勾选。
- **正确修复**:弃用 ele-pro-table 的 selection 机制,将 `UserSelectModal.vue` 重写为 antd `a-table`
- 受控 `selectedRowKeys`watch props.visible 时用 `props.selectedUserIds` 初始化 → 打开弹窗已选即勾选
- `rowSelection` computed`{ columnWidth:48, selectedRowKeys, onChange }`
- datasource 用 `pageUsers` 包成 Promise,分页用 antd `pagination` reactive`@change` 调 reload
- search `a-input-search`@search/@pressEnter reload
- confirm emit `selectedRowKeys`
- 字段已与 User 模型对齐(userId/realName/mobile/nickname/organizationName),`PageResult<T>={list,count}`。
- 改动文件:`src/views/special/zone/components/UserSelectModal.vue`(整体重写约 130 行)。
- 部署:刷新页面(`npm run dev` 热更新)或重新构建 admin 前端即可。无需后端改动。
## 专区小程序码(扫码直接进入专区)
- 需求:/special/zone 专区管理加「二维码」入口,生成该专区的小程序码,用户微信扫码直接进入小程序对应专区。
- 后端(guilixu-java `ShopHomeSectionController.java`):新增 `GET /api/shop/shop-home-section/{id}/qrcode`
- 调微信 `getwxacodeunlimit` 生成无限场景码;`scene=sectionId=xxx``page=pages/shop/section-detail`;按专区 `tenantId` 经 `WxMiniappAccessTokenService.getAccessToken(tenantId)` 取 tokenprod 用 `release` 否则 `trial`(与现有 getOrderQRCodeUnlimited 一致)。
- 复用 Hutool `HttpRequest` 调微信,返回 PNG 流;微信出错时返回 JSON 并识别 `application/json`。
- 用户端小程序(guilixu-taro `pages/shop/section-detail.tsx`):解析 `useRouter().params.scene`(小程序码扫码透传的 `sectionId=xxx`),兼容原 `?sectionId=` 普通跳转。
- 管理后台(guilixu-admin `views/special/zone/index.vue` + `api/shop/shopZone`):列表操作列加「二维码」按钮 → 弹窗显示小程序码图片;新增 `getSectionQrcode(id)`responseType blob → objectURL,遇 JSON 错误解析 message 并 reject)。
- 部署:① 重新编译部署 guilixu-javashop-api)使新接口生效;② 重新构建前端 admin 与 taro 小程序(section-detail 需重新发布/体验版,因小程序码 page 指向已发布页面)。
- 注:本机无 maven,后端未本地编译;改动完全复用 WxLoginController 既有 getwxacodeunlimit 写法。
- 限制:getwxacodeunlimit 的 page 必须已存在于小程序(pages/shop/section-detail 已在 app.config 的 pages 列表);scene 仅限可见字符、≤32 位,`sectionId=xxx` 满足。
## 专区小程序码报 41030 invalid page 修复
- 现象:后台点「二维码」生成小程序码,微信返回 `{"errcode":41030,"errmsg":"invalid page"}`。
- 根因:原代码按 profile 决定 env_version——active=dev 时走 `trial`(体验版)。微信 41030=所请求版本的小程序代码包里**没有该页面**。页面 `pages/shop/section-detail` 在源码 app.config 存在,但体验版小程序没上传含此页面的代码 → 失败。
- 修复:
- 后端(ShopHomeSectionController.getSectionQrcode):去掉按 profile 判断,改为 `envVersion` 请求参数,`@RequestParam(defaultValue="release")``env_version` 直接用该参数(release/trial/develop)。
- 前端(admin):`getSectionQrcode(id, envVersion='release')` 支持传参;弹窗加「正式版/体验版/开发版」RadioGroup(默认正式版),切换即重新生成。
- 关键前提(务必告诉用户):**生成成功 ≠ 扫码能进专区**。
1. 生成码所请求的版本(正式/体验)其代码包必须包含 `pages/shop/section-detail` 页面,否则仍 41030。
2. 扫码能正确进专区,还要求把「能解析 scene 的 section-detail.tsx」发布到**同一版本**:正式版需发版、体验版需上传体验版、开发版需开发者工具预览。
3. 若默认正式版仍 41030,说明线上版本也没有此页面 → 需先发布小程序(该页面虽在 app.config,但可能未上线)。
- 部署:需重新编译部署 guilixu-java + 重新构建发布 taro 小程序(到目标版本)。
## 专区白名单「添加/移除」功能确认 + tenantId 修复
- 现状确认:`shop_home_section_user` 白名单的「添加/移除」功能**此前已完整实现**,本次只是排查确认:
- 后端 `ShopHomeSectionController``GET /{id}/users`(查)、`PUT /{id}/users`(覆盖式保存);`ShopHomeSectionUserService.listBySectionId` → `selectBySectionId`(XML)。
- 前端 `index.vue`:列表「白名单」按钮 → `openUsers` 调 `listSectionUsers` 加载已选 → 弹窗;`onSaveUsers` 调 `setSectionUsers` 保存。
- `UserSelectModal.vue`:用 `pageUsers`(sys_user) 选人,勾选=添加、取消=移除、保存=覆盖提交;columns 与 User 模型字段(userId/realName/mobile/nickname/organizationName)已核对匹配。
- **真 bug 修复**:后端 `setUsers` 新建 `ShopHomeSectionUser` 时**未设 tenantId**(实体有该字段,本项目手动多租户)。若表 `tenant_id` 有 NOT NULL 约束会插入报错;即便不报错也缺租户归属。
- 修复:从专区 `homeSectionService.getById(id).getTenantId()` 取租户,set 到每条白名单记录(`ShopHomeSectionController.setUsers`line ~119)。
- 查询侧(`selectBySectionId`)按 `section_id` 隔离,未动;下单强校验 `checkPermission` 也按 section_id 隔离,一致。
- 用户体系统一结论(沿用 01:50 澄清):买家=sys_user`pageUsers` 选人正确,白名单 user_id 与下单校验 userId 同体系。
- 部署:重新编译部署 guilixu-javashop-api)生效后,后台 /special/zone 每条专区「白名单」即可勾选添加、取消移除并保存。
## 白名单弹窗搜索报错修复([object PointerEvent] 误作 limit 传给后端)
- 现象:白名单弹窗点搜索触发 400:`BindException ... Field error in object 'userParam' on field 'limit': rejected value [[object PointerEvent]]`。
- 根因:重写后的 `UserSelectModal.vue` 模板 `@search="reload"` / `@pressEnter="reload"`。antd `a-input-search` 的 `search` 事件签名是 `(value, event)`,故 `reload` 实际收到 `reload(搜索词, PointerEvent)``reload(page, limit)` 把第二个参数当 `limit` 传给 `pageUsers``pageSize` 变成 PointerEvent → 序列化 `[object PointerEvent]` 传给后端 `limit` 字段 → 类型转换失败。
- 修复:模板改为 `@search="() => reload(1)"` / `@pressEnter="() => reload(1)"`,只传页码、不传事件对象;`reload(1)` 时 `limit=undefined` → `pageSize` 回退 `pagination.pageSize`。
- 改动:`UserSelectModal.vue` 两行模板。
- 部署:刷新 admin 前端(`npm run dev` 热更新或重新构建)即可。
## 白名单保存报 Duplicate entry(逻辑删除×唯一索引死局)
- 现象:保存专区白名单 `PUT /{id}/users` 报 `SQLIntegrityConstraintViolationException: Duplicate entry '1-35771-1' for key 'shop_home_section_user.uk_section_user'`,失败 SQL 是 `UPDATE shop_home_section_user SET deleted=1 WHERE tenant_id=10606 AND deleted=0 AND section_id=?`MP 逻辑删除)。
- 根因(已确证):`add_shop_home_section.sql:40` 定义 `UNIQUE KEY uk_section_user (section_id, user_id, deleted)`——唯一索引**包含 deleted 列**;而实体 `ShopHomeSectionUser` 上有 `@TableLogic`(逻辑删除)。`setUsers` 用「`remove` 全部 + `saveBatch`」覆盖式保存,`remove` 被 MP 转成 `UPDATE SET deleted=1`;历史多次保存已囤积 `deleted=1` 旧行,下次再把某条 `deleted=0` 行改成 `deleted=1` 就与旧 `deleted=1` 行撞唯一键。
- 修复(绕过逻辑删除,用物理删除):
- `ShopHomeSectionUserMapper.java` 新增 `int physicsDeleteBySection(@Param sectionId, @Param tenantId)`
- `ShopHomeSectionUserMapper.xml` 新增 `<delete id="physicsDeleteBySection"> DELETE FROM shop_home_section_user WHERE section_id=? <if tenantId not null>AND tenant_id=?</if> </delete>`(自定义 SQL,MP 不会注入逻辑删除 → 真物理删除);
- `ShopHomeSectionController.setUsers` 把 `homeSectionUserService.remove(new QueryWrapper...eq("section_id", id))` 换成 `homeSectionUserMapper.physicsDeleteBySection(id, currentTenantId)`,再 `saveBatch`。每次保存前把该专区所有行(含 deleted=1 残留)真正删掉再插新行,不再囤积,不撞键。
- `selectBySectionId` 已带 `deleted=0` 过滤,查询侧无影响。
- 说明:此表使用逻辑删除 + 含 deleted 的唯一索引本就是反模式;本修复用物理删除规避,安全且低风险(本机无 maven 未编译)。前端无需改动。
- 遗留(无害):其他未再保存过的专区可能仍有历史 deleted=1 重复行,因 `selectBySectionId` 过滤 deleted=0 且保存已改物理删除,不影响业务;如需彻底清可用 `DELETE FROM shop_home_section_user WHERE deleted=1 GROUP BY section_id,user_id HAVING COUNT(*)>1` 之类语句(谨慎,先备份)。
## 白名单弹窗改为商品式(打开列已加、搜索追加、单条即时增删)
- 用户反馈:旧版覆盖式勾选弹窗「勾了别的用户后前面勾过的又不见了」。`UserSelectModal.vue` 重写,统一成与专区「商品」抽屉一致的交互:打开只列已添加的白名单用户;搜索手机/姓名/昵称出候选,点「+添加」即时入库、点「移除」即时删。
- 根因(旧交互):覆盖式保存依赖 `selectedRowKeys` 跨打开/重渲染保持,易丢;且 `setUsers` 覆盖写遇到唯一键冲突会整体失败。商品式用「单条即时增删」彻底规避。
- 后端(guilixu-java `ShopHomeSectionController`):
- `GET /{id}/users` 改为**联表返回用户详情** `List<User>`realName/mobile/nickname/organizationName/userId),不再只返回关系行(旧返回 ShopHomeSectionUser 仅含 userId,前端展示空白)。新增注入 `UserService`,先取关系行再 `userService.listByIds` 取详情。
- 新增 `POST /{id}/users` `addUsers`:追加用户,先查已存在 userIds 跳过(防 uk_section_user 唯一键冲突),再 `saveBatch`,带 tenantId。
- 新增 `DELETE /{id}/users` `removeUsers`:物理删除指定用户,调 `homeSectionUserMapper.physicsDeleteBySectionAndUsers(id, userIds, tenantId)`(新增 mapper/XML 方法,foreach user_id IN,绕过 @TableLogic)。
- 旧 `PUT /{id}/users` `setUsers`(覆盖式)保留兼容,前端不再使用。
- 原 `users` 用的 `homeSectionUserService.listBySectionId` 不再调用(改用 `list(new QueryWrapper...)`)。
- 前端:
- `api/shop/shopZone/index.ts``listSectionUsers` 返回类型改 `User[]`import system `User`,移除 `SectionUser`);新增 `addSectionUsers(id, userIds)`(POST)、`removeSectionUsers(id, userIds)`(DELETE)。
- `components/UserSelectModal.vue` 整体重写:props(`visible`,`sectionId`),内部 watch(visible) 调 `listSectionUsers` 加载已加名单;`pageUsers({keywords})` 搜索候选(排除已加);`addOne`/`removeOne` 即时调接口并重载;移除 footer 与 `@confirm`,改用 `:footer="null"`。
- `index.vue`:弹窗调用去掉 `:selectedUserIds` / `@confirm``openUsers` 简化为只置 `currentSectionId`+开弹窗;删除 `onSaveUsers`、`currentUserIds`、未用 import`setSectionUsers`/`listSectionUsers` 在 index 内已无引用)。
- 后端 `users` 关键字搜索:sys_user `UserParam.keywords` 已覆盖 username/user_id/nickname/real_name/alias/phone(含手机、姓名、昵称),满足需求。
- 部署:重新编译部署 guilixu-javashop-api+ 重新构建 admin 前端。本机无 maven 未编译;改动为标准 MP/MyBatis 用法,风险低。
## 专区白名单弹窗改为右侧抽屉(modal → drawer
- 用户要求:专区「白名单」入口的交互与同页「商品」一致,改成右边弹出的抽屉。
- 改动:`src/views/special/zone/components/UserSelectModal.vue` 外层容器由 `<a-modal>` 改为 `<a-drawer placement="right" :width="820">`,内部搜索/候选/已添加列表逻辑原样保留,props/emits(`visible`/`sectionId`/`update:visible`) 不变。
- `index.vue` 中 `<UserSelectModal v-model:visible="showUser" :sectionId="currentSectionId" />` 调用方式无需改动(与商品抽屉同为右侧弹出)。
- 部署:重新构建 admin 前端(npm run dev 热更新或 build)即可,无需后端改动。
## 专区白名单手机号改为不脱敏
- 需求:专区「白名单」抽屉里手机号不要脱敏显示。
- 根因:系统 User 实体 `mobile` 是 `@TableField(exist=false)` 的脱敏字段(`getMobile()` 返回 `DesensitizedUtil.mobilePhone(phone)`);`phone` 才是 DB 真实号码,无 `@JsonIgnore`、无全局脱敏 → 接口同时返回 `phone`(真实) 与 `mobile`(脱敏)。
- 改动:`UserSelectModal.vue` 两处展示由 `u.mobile` 改为 `u.phone`(候选行 `u.phone || u.mobile || '-'`;表格列 `dataIndex:'phone'` + `customRender` 回退 `record.mobile`),后端无需改动。
- 部署:重新构建 admin 前端即可。
## 专区商品抽屉分页「20/100 条/页」不可用修复
- 现象:专区商品抽屉右下角切 20/100 条/页不生效,始终只展示 10 条。
- 根因:`src/views/special/zone/index.vue` 中商品表格的 `:pagination` 把 `pageSize: 10` 写死,且 `onChange` 只接收 `page` 参数、未处理 `pageSize`;翻页事件里还误写成 `goodsPage = page`(应 `.value`)。
- 修复:
- 新增 `goodsPageSize = ref(10)`
- `loadGoods` 请求参数 `limit` 改为 `goodsPageSize.value`
- 分页配置改为 `pageSize: goodsPageSize`、`showSizeChanger: true``onChange(page, pageSize)` 同时更新 `goodsPage.value` 与 `goodsPageSize.value` 后调 `loadGoods`。
- 改动文件:`src/views/special/zone/index.vue`。
- 部署:重新构建 admin 前端即可。
## 商城商品导出「完全无反应」修复(/shop/shopGoods 导出功能不可用)
- 现象:/shop/shopGoods 点「导出xls」完全没反应(无下载、无报错提示)。
- 根因:`src/views/shop/shopGoods/index.vue` 第236行 `const loading = ref(true);`,而 `handleExport` 第一行 `if (loading.value) return;`。`loading` 这个变量只有 `handleExport` 自己会改(导出中设 true / 结束或失败设 false),列表加载的 `reload()`(只调 `tableRef.reload`)、`query()`(只加载分类树)从不碰它 → 页面加载后 loading 永远是初始的 `true` → 导出首行被永久拦截、逻辑从未执行。
- 已排除项:按钮 emit、后端 `/shop/shop-goods` 返回 `ApiResult<List<ShopGoods>>`、xlsx@0.18.5 依赖、vite `optimizeDeps.include:['xlsx']` 均正常——不是接口/依赖/打包问题。
- 修复(按用户"修守卫+带筛选导出"):
1. `index.vue` 第236行 `ref(true)` → `ref(false)`loading 恢复成"导出中防重复点击"语义,首击可进入)。
2. 导出携带当前筛选:`search.vue` emit 签名 `(e:'export', where?: ShopGoodsParam)``handleExport` emit('export', where)`index.vue` `handleExport(where?)` 调 `listShopGoods(where || {})`(原 `listShopGoods({})` 导出全部、忽略筛选)。where 来自 search.vue 的 `useSearch<ShopGoodsParam>`,与列表 datasource 用的 where 同源,完全复现筛选。
- 改动文件:`src/views/shop/shopGoods/index.vue`、`src/views/shop/shopGoods/components/search.vue`(共5处小改)。
- 部署:重新构建 admin 前端(npm run dev 热更新或 npm run build)即可,无需后端改动。
- 验证清单:① 进 /shop/shopGoods 点「导出xls」应弹"正在准备导出数据"并下载 xlsx;② 先筛选分类/关键词/状态再导出,xlsx 内容应与筛选结果一致;③ 导出中连点不应重复触发(loading 防重生效)。
## 商城商品导出增加「封面图」路径列(用户改主意:不内嵌图片,只导出路径)
- 背景:用户原想 Excel 内嵌封面图(前端 fetch OSS 图→base64→xlsx !images),但 OSS(oss.wsdns.cn) 与 admin(websoft.top) 跨域,浏览器 fetch 会被 CORS 拦截;且逐张下载会导致导出变慢、xlsx 体积暴涨。用户明确改为「图片导出路径就行了」——即 Excel 里加一列封面图 URL 文本。
- 实现:`src/views/shop/shopGoods/index.vue` 的 `handleExport` 导出列末尾新增「封面图」列,值为 `ensureFullUrl(goods.image)``@/utils/image` 的 ensureFullUrl:相对路径补成 `https://oss.wsdns.cn/...` 完整 URL,已是 http(s) 原样返回)。表头/每行/`!cols` 列数均由 10 增到 11,封面图列宽 wch:50。
- 补 import`import { ensureFullUrl } from '@/utils/image';`(紧接 xlsx 的 import 之后)。
- 改动文件:`src/views/shop/shopGoods/index.vue`handleExport 内共 4 处:import / 表头 / forEach / !cols)。
- 说明:Excel 单元格中是 URL 文本,运营复制/点开即可在浏览器看原图;不是内嵌缩略图。OSS 图片无 CORS 依赖,导出不再受跨域影响。
- 部署:重新构建 admin 前端即可,无需后端改动。
## 注册页复制:website-admin → guilixu-admin 的表选型分析
- 用户想复制 website-admin 的 `/register`(四步建站:邮箱验证→企业信息→站点初始化→开通)到 guilixu-admin。
- 关键事实:
- website-admin 注册「创建站点」调 `@/api/cms/site.createSite` → `POST /api/cms/cms-website`,落 **modules.cms_website**mp-javacms-api.websoft.top)。`superAdminRegister`(建租户)走其主后端 SERVER_API_URL。
- guilixu-admin 现有 `views/passport/register|register2` 调 `createCmsWebSite` → `SERVER_API_URL/superAdminRegister`;但 **guilixu-java 里无 superAdminRegister 方法**(仅 SecurityConfig 放行 `/api/cms/website/createWebsite`)→ 该端点生产上实际未接通/404。
- guilixu-admin 的 cms 站点管理 UI`api/cms/cmsWebsite`)走 `CMS_API_BASE_URL=cms-api.websoft.top` = **mp-java/modules**,即 modules.cms_website。
- **db_guilixuguilixu-java)没有 cms_website 表**:全仓搜 CmsWebsite/cms_website 仅 MybatisPlusConfig 一行被注释的 `"cms_website"`;有 `shop_setting``@TableName("shop_setting")`,字段 category/settingKey/settingValue = K/V 商城配置,非站点主记录)。
- 结论(待与用户确认):复制注册应选 **modules.cms_website**(走 CMS_API_BASE_URL,与 website-admin 的 createSite、guilixu-admin 现有 cmsWebsite UI 完全一致),**不要**用 db_guilixu.shop_setting(语义不符)+ db_guilixu.cms_website 根本不存在。另需决定:guilixu-admin 是单租户(10606)后台,注册多半只「在 10606 下建微官网」而非新建租户 → 应跳过 superAdminRegister,仅保留 createSite(cms_website) 一步。