Vue 组件封装与可见性
组件封装:.sync / visible / v-if
问题: 弹窗/抽屉子组件在父组件数据未就绪时提前初始化,导致 props 监听失效;关闭后可见状态与父组件不同步。
原因: 仅用父组件一个 visible 不够稳;子组件内部 Drawer 的 update:visible 需要回写父级;data 与 computed 不能同名,否则计算属性失效。
做法:
- 从 HTML 梳理功能所需数据/方法/子组件,写入 props;父组件不需要的删掉。
- 父传
:dshow.sync="dshow",子内用内部变量接:visible.sync="detailShow",再 watchdshow/detailShow双向同步。 .sync等价于:visible+@update:visible。- 或用 computed getter/setter 兼监听与回写。
- 两种稳妥模式:①
visible+v-if(子组件不必自己控显隐);② 双visible(父属性名与子 props 名对齐)。本质都是「两个可见状态」:仅打开时才创建子组件,父数据就绪后再挂载。







父组件调用子组件函数
问题: 父组件需要直接调用子组件方法。
做法: 通过 $refs 拿到子实例后调用其方法(见截图)。
mounted + watch 获取父组件数据
问题: 子组件既要处理首次打开/刷新时已有数据,也要处理后续动态点开详情时数据变化。
原因: 仅 mounted 或仅 watch 都会漏场景。
做法: mounted 处理首次已有数据;watch 处理动态变更。
嵌套与动态增减表单
问题:(备忘)嵌套表单与动态增减字段。
说明: 创建题目页与答题页这类场景会用到嵌套 + 动态增减。
做法: 按业务拆嵌套表单项,用数组驱动增删。
v-for 遍历嵌套对象
问题: v-for 遍历数组里嵌套对象时字段取不到。
做法: v-for="item in 数组",再用 item.对象字段 访问。
el :value 与 v-model 类型一致
问题: el-select 想默认选中某 option,却显示成字符串 "1" 或选不中。
原因: option 的 :value 是 number,而 v-model 绑定成了 string,类型不一致。
做法: 默认值也用 number(如 parseInt(this.$route.query.type)),与 option value 类型对齐。

$modalForm 等自定义插件方法
问题:(备忘)项目里自定义弹窗/表单插件。
说明: 概念备忘。
做法: 使用 $modalForm 等挂在实例上的自定义插件方法;配合 $refs / $nextTick 做组件交互。
响应式与渲染更新
响应式原理与 render / watchEffect / $set
问题: 何时会触发重新运行?为何原始类型「响应式」会失效?
原因: 响应式依赖「对象形态的数据」+「函数运行期间读到了该数据」;原始类型在部分场景(如 VueUse 取 props)会失效。
做法:
- 用 render / watch(Vue2)/ watchEffect(Vue3)/ computed 监听响应式对象(ref、reactive)。
- render、watchEffect 一开始就会同步跑一遍。
- 给对象新增属性用
$set,否则视图不更新。
$set 的使用
问题: 给对象/数组新增属性后视图不更新;或要把后端列表重组为「姓名→年龄」映射。
原因: Vue2 无法探测直接赋值的新属性;需 $set(target, key, value)。
做法:
- 新增:
this.$set(this.myInfo, 'age', 24),不要this.myInfo.age = 23。 - 重组:遍历 info,
$set(this.newInfo, item.name, item.age)得到 name→age。


删除对象属性与 $forceUpdate
问题: 用 delete 删掉对象键后界面不刷新,又不想再请求接口。
原因: 直接 delete this.tableDetail[no] Vue 侦测不到。
做法: 用 previousNumber 记上次审批单号;@change 调 setFinance,对比当前 currentAuthNumbers 做增删;删除后调用 $forceUpdate() 强制重渲染。

fee 对象重组与 Object.keys
问题: 返回数据是对象或数组,要把 id/fee_id/currency_id/money_input/remark 收进 fee,再随 dataForm 提交。
原因: 对象结构需按 key 下钻;数组可直接遍历;后端若不需要 fee 则不必收集。
做法:
- 对象:
Object.keys遍历,再 foreach 推入 feeItems。 - 数组:直接遍历。
- 不需要 fee:直接发 dataForm 的 money_input、remarkWriteoff。




JS 对象间映射(fee_type)
问题: table 遍历 B,却要用 A 上的 fee_type(两边通过共同 no 关联)。
原因: 两对象字段分散,需要先建映射再查。
做法: 遍历 A 建 feeTypeMap[no] = fee_type,模板里 {{ feeTypeMap[key] }}。



数组去重
问题: finance 列表里 uid 重复,需要唯一 uid 列表。
做法: [...new Set(res.data.finance.map(item => item.uid))]。
Array.isArray 判断数组
问题: typeof 得到 "object",分不清对象和数组。
原因: JS 里数组的 typeof 也是 "object"。
做法: 用 Array.isArray(...)。
null / undefined 与 0 的比较
问题:(备忘)null/undefined 和 0 怎么比。
说明: 概念备忘。
做法: null !== 0、undefined !== 0、null != 0、undefined != 0 均为 true;Number(undefined) 是 NaN,NaN !== 0 也是 true。
price_edit_open !== false
问题: 接口可能返回 true/false/undefined/null,开关默认该开还是关?
原因: 直接赋值会把 undefined/null 原样带下去;业务要「只有明确 false 才关」。
做法: this.price_edit_open = res.data.price_edit_open !== false → false 才为 false,其余(含未返回)为 true。
浮点数相加精度 bug
问题: 123.08 + 1.1 期望 124.18,截断后却得到 124.17。
原因: 0.08、0.1 二进制无限循环,IEEE754 只能存近似值,和为 124.17999999999999,截断变 124.17。
做法: ① 整数分/厘累加再除 100/1000;② decimal.js / big.js;③ 四舍五入。
JS 默认参数
问题:(备忘)函数参数想有默认值。
说明: 概念备忘。
做法: function test(a, b = 10):有传用传入值,没传用默认;打开详情可写 test(1) 不必 test(1, 10)。
encodeURIComponent
问题:(备忘)URI 组件编码。
说明: 概念备忘。
做法: encodeURIComponent(str) 把非字母数字(除 -_.!~*'())编成 % + 两位十六进制(UTF-8)。
Bootstrap 栅格断点
问题:(备忘)多端栅格适配。
说明: 概念备忘。
做法: xs<576 / sm≥576 / md≥768 / lg≥992 / xl≥1200;用 col 的 xs/sm/md/lg/xl 列数(如大屏 8、小屏 24 整行)。

路由守卫与页面标题
路径参数与 Query 参数
问题: path 参数和 query 参数怎么写、有何区别。
做法: 路径参数写在 path 段;Query 用 ?key=。见截图对比。


路由标题:is_draft 与 beforeRouteEnter / Update
问题: 采购管理与草稿共用页、靠 is_draft 区分,但 meta 标题要不同。
原因: 只写 beforeRouteUpdate → 首次直达带 query 不触发;只写 beforeRouteEnter → 同组件内切参数不触发。
做法: 两个守卫都写:按 to.query.is_draft === '1' 设「采购计划-草稿」否则「采购计划管理」。

拆分新建 / 编辑项目路由
问题: 新建与编辑共用一个路由配置时,页签标题/权限混乱(如编辑却显示新建)。
原因: router 里只配了一条路由。
做法: 拆成 newProject 与 editProject 两条(可共用同一组件文件),各自 meta.title / auth。




客户 list_trade / list_clearance 路由
问题: 贸易客户与清关客户列表要分路由、分标题,组件可复用。
做法: 配 list_trade(meta.is_trade: 1)与 list_clearance(is_trade: 2),同组件 ClientManage,keepalive + 各自 title。
国际化 i18n
下拉选项按 locale 切换与 $t 占位符
问题: 接口返回中英文字段的下拉,要随当前语言切换;静态文案与带变量确认文案怎么翻。
原因: 选项 label 来自接口字段;静态文靠语言包;占位符名不能带空格。
做法:
const currentLang = this.$i18n.locale,map 时label: currentLang === 'en' ? (name_en || name) : name。- 静态:
{{ $t('message....') }}。 - 占位符:词条用
{source}/{target}(不能{ source }),调用$t(key, { source, target })。 - watch
$i18n.locale重新拉选项。$i18n来自/src/i18n/index.js。





项目中,中英语言切换实现:
📎2026-09-14-default-locale-english-design.md
axios 传参(params / data)
params 与 data:数组传参
问题: 要把数组参数正确发给后端(尤其 PUT)。
原因: GET/部分场景 params 会序列化成 ids[]=1&ids[]=2;body 应用 data。
做法: PUT 等 body 用 data 而非 params: data。
API 请求的五种参数格式
问题:(备忘)项目里常见的五种入参写法。
说明: 概念备忘。
做法: ① 多参数(多用于 GET);② 对象 {params, data, options};③ 请求体(POST/PUT,含简写);④ 路径+对象混合;⑤ REST 路径参数。









多选下拉数组序列化
问题: 多选提交出现 industry_ids[]=… 等非预期格式,后端解析失败或格式不对。
原因: multiple 使 v-model 成数组;若用 params 发送,axios 默认 PHP 风格 industry_ids[]=13&industry_ids[]=14。
做法:
- 需要字符串:调用前
industry_ids: this.formData.industry_ids.join(',')。 - 需要真正数组 JSON:改用
data(body)。 - 口诀:params=信封地址(筛选/分页);data=信封内容(提交/复杂 JSON)。GET/DELETE 一般用 params;POST/PUT/PATCH 一般用 data。GET 用 data 常传不出去。



传参为 0 时勿用假值判断
问题: 接口合法值是 0,前端却丢成空。
原因: 0 || '' 在 JS 里把 0 当假值。
做法: 显式判空:this.source = (res.source !== null && res.source !== undefined) ? res.source : ''。
接口封装与 axios 层
问题: API 要同时支持 .then 与 async/await。
做法: API 函数 return request({...}) 返回 Promise;request.js 用 axios.create + 请求/响应拦截(token、status!==200 则 reject)。

Promise 与 async
Promise 链式 vs async/await
问题: Vue 里调接口用哪种写法。
说明: 两者本质都是 Promise;async/await 是语法糖,现代首选。
做法: .then/.catch 链式,或 async + try/catch/finally;接口必须返回 Promise。

Promise.all / race / allSettled
问题: 多请求并发时选哪个聚合 API。
说明:
- all:全成功才成功,一败俱败(快失败);适合首屏缺一不可。
- race:谁先 settle 听谁;适合超时、多源竞速。
- allSettled:全结束再看各自 status;适合尽量多拿结果、统一关 loading。
做法: 订单详情+收支列表并发用 allSettled;初始化用户+权限+配置用 all;超时用 race(fetch, timeout)。注意 all 短路后其他请求仍在跑;allSettled 要判 status 再取 value。
Promise 实用模版
问题:(备忘)常见并发/超时/降级/重试写法。
说明: 概念备忘。
做法:
- 首屏
Promise.all+ finally 关 loading。 - 多区域
allSettled按 status 分别赋值/警告。 withTimeout=race(promise, timeoutPromise)。- 主接口失败 try 兜底接口。
retry(fn, times, delay)循环重试。- 当前场景:先开 loading,起两个 promise,
await allSettled,再关 loading。 避坑:勿forEach(async)控串行;all 结果顺序=输入顺序;用 finally 或 allSettled 后统一关 loading;频繁刷新加「最后一次请求生效」。
表格勾选与跨页选择
分页勾选数据丢失与跨页回显
问题: 翻页后勾选丢失;或 ids 里还有、勾选框却空;下载/打印后需清空。
原因: 表格 DOM 随 dataList 重建,仅当前页 selection 不够;$nextTick 前 toggle 找不到新行;reserve-selection alone 仍有局限。
做法:
- 维护全局
selectedRows/selectedIdsSet+ 提交用ids。 @select/@select-all→handleSelectRow(selection):对当前页 ids,选中则加入集合、取消则移除,再ids = [...selectedIdsSet]。getList赋值后$nextTick→restoreSelection:对当前页每行toggleRowSelection(row, true/false)。- 下载/打印成功后清空选择。
- Element Plus 的
row-key+reserve-selection可作辅助,但仍建议手动集合 + 回显。













列表展示与行内编辑
列表 on-change 与 fastEdit 误触发
问题: 查询刷新时批量打 fastEdit;清空单元格时 fastEdit 打两次。
原因: getList 更新 v-model 触发 on-change;清空时 clear + v-model 同步各触发一次。
做法:
isLoadingData:拉数前 true,双$nextTick后再 false;updateProductField开头若 loading 则 return。- 防抖:记录
rowId_field_value时间戳,100ms 内重复跳过。 - 或
@on-clear→handleClear传'',updateProductField对undefined直接 return。 - on-change 用事件参数:
(value) => updateProductField(row, field, value),勿依赖row.xxx时序。











关闭弹窗后再刷新列表
问题: 操作成功后立刻开列表仍是旧数据,再开才是新数据。
做法: 成功回调里先关弹窗(formShow = false),再 getList()。
el-table :data 必须是数组
问题: :data 绑对象时报错/不渲染。
原因: el-table 的 data 只接受数组。
做法: tableDetail 为对象时 v-for="(value, key)" 且 :data="value"(value 已是数组);为数组时 :data="[item]"。
iview Table render 可编辑单元格
问题: iview 表格列要可编辑输入。
做法: columns 的 render(h, params) 返回 h('Input', { props, on: { 'on-change': e => formData.contacts[params.index].name = e.target.value } });params.row/index/column 可用。
列表最后一行单独样式
问题: 只要最后一行特殊样式。
做法: 模板三元 + JS 函数返回布尔,决定是否加 class。
操作后更新行状态
问题: 行操作成功后要改该行状态,不必整表重拉。
做法: 在成功回调里直接改当前行字段(见截图)。

状态字段映射
问题: 后端 status 为 0/1/-1/100,要显示文案和颜色。
做法: getStatusText / getStatusColor 用 map;模板里 Tag 绑定二者。


id→name 供应商映射(iview + element)
问题: 行里只有 supplier_id,要显示 fullName,且数据量大不能手写 statusMap。
做法: 拉供应商列表建 supplierMap[id]=fullName。 - iview:render 里 h('span', this.supplierMap[params.row.supplier_id])。 - element:formatter / 作用域插槽 / computed 预处理加 supplier_name。






iview Select 宽度与 filterable
问题: 未开搜索时选框可随 label 自适应,开 filterable 后宽度不再自适应。
说明: 组件行为备忘。
做法: 需要固定/百分宽时手动设 style="width:…";知悉 filterable 会关掉自适应。
欠款报表:动态列与多级表头
问题:(备忘)欠款报表要动态列 + 多级表头。
说明: 源笔记仅有主题名,无具体实现细节。
做法: 按业务配置动态 columns / 多级表头(待补实现笔记)。
表单、数字与校验
下拉框「不限」与 null
问题: 「不限」用空字符串时,初始化若也设 '',占位符不生效且默认像已选不限;接口选项会覆盖手写「不限」。
原因: 空串与「不限」option value 冲突;直接赋值数组会冲掉前置不限项。
做法: 初始用 null 让 placeholder 生效;请求前把 null 滤掉;接口选项用中间变量拼 [不限, ...res] 再赋给 options。






千位分隔符与 toLocaleString
问题:(备忘)金额千分位、百分比、货币、位数、时间格式化。
说明: 概念备忘。
做法: 正则 toFixed(2) 后整数部 replace(/\B(?=(\d{3})+(?!\d))/g, ',');或 num.toLocaleString() / 带 style:'percent'|'currency'、最小最大小数位、日期 toLocaleString。







表单验证 trigger 与 number 类型
问题: 输入数字后校验失败且提示为空;不知用 blur 还是 change。
原因: 默认 rule type 是 string;number 值类型不匹配。
做法: Input 用 trigger:'blur';Select/Date/Checkbox/Radio 用 change;数值字段加 type:'number'。

Select 严格相等 === 类型匹配
问题: Select 有值却显示不出选中项。
原因: 用 === 匹配,"1" !== 1。
做法: option 数组元素类型与 v-model 一致;后端是数字则用数字/parseInt,初始别用 '' 而用 0(若业务允许)。
validator.js 与 props validator
问题:(备忘)邮箱/手机/URL 校验,以及限制 prop 枚举。
说明: 概念备忘。
做法: 第三方 validator.isEmail/isMobilePhone/isURL;props 里 validator: v => ['trade','order','commission'].includes(v)(Vue 内置,非同一库)。

导出、打印与下载
打印弹窗:先 window.open 空白页
问题: 异步拿到打印 URL 后再 window.open,被浏览器当弹窗拦截。
原因: 非用户手势同步上下文打开窗口易被拦。
做法: 点击时立刻 printWindow = window.open('', '_blank');拦截则提示;成功后 printWindow.location.href = res.data.url;失败/!url 则 close() 并报错。

打印与导出方法
问题: 打印/导出文件怎么触发下载。
做法:
- 打印:
window.open(res.data.url)(或空白页方案)。 - 导出:① 后端返回 URL →
location.href;②<a download>;③window.open;④ Blob +URL.createObjectURL+ a.click + revoke(适合文件流)。






打印 / 导出 icon
问题:(备忘)按钮图标。
做法: el-icon-printer / el-icon-download。
uni-app / 小程序 / CRM
getSystemInfoSync
问题:(备忘)取状态栏高度、屏幕、平台等。
做法: uni.getSystemInfoSync(),Android/iOS/H5 返回字段不同(如 statusBarHeight、platform)。
image aspectFill / widthFix
问题:(备忘)小程序 image mode 怎么选。
说明: 缩放/裁剪共 13 种;常用 aspectFill(短边填满可裁切)、widthFix(宽固定高自适应)等。
做法: 按展示需求选 mode(见表截图)。
swiper iOS 高度塌陷
问题: iOS 上 swiper 默认高 150px,图片上半被裁。
原因: iOS 默认高度;#ifdef APP-IOS 打包后可能不生效。
做法: 用 getSystemInfoSync().platform === 'ios' 动态算高度/marginTop(可参考 statusBarHeight*2+110)。
safeAreaInsetBottom
问题: iOS 弹窗底部多一块空白。
原因: u-popup safeAreaInsetBottom 默认为 true,预留底部安全区。
做法: 不需要留白时设 false;需要防手势条遮挡则保持 true。
Token:Vuex + 本地存储
问题: 重启 App 丢失登录态;登录页重复进。
做法:
- 登录成功 mutation:写 Vuex +
uni.setStorageSync。 onLaunch:getToken()有则 commit 恢复。- 登录页
onLoad:本地已有 token 则reLaunch首页。




懒加载方案
问题: 是否等全部图加载完再揭开整页。
原因: 整页等齐拖垮 TTFB、浪费流量,不适合信息流。
做法: 骨架/固定槽位 → 接口先渲染文案 → 列表 lazy-load → 可选仅预加载 Banner/分类图标。
Google Play 照片选择器与 excludePermissions
问题: 上架被拒:不能靠宽泛媒体权限/uni.chooseImage 扫相册。
原因: Play 政策要求系统照片选择器,target 高版本勿申请 READ_MEDIA_*(除非核心功能无法替代)。
做法: 优先 uni.chooseMedia 或 Photo Picker 插件;manifest 加 Camera 模块;excludePermissions 排除 READ_MEDIA_IMAGES/VIDEO 等;注意 abiFilters、min/targetSdk。
业务模块备忘
核销 → 审批 → 任务 → 收支
问题:(备忘)核销业务链路怎么点进去。
说明: 领域路径备忘。
做法: 核销管理 → 审批单号 → 任务单号 → 任务详情 → 收支费用 → 收支详情。
Spring Boot 连接数据库
问题: 如何用 application.yml 连库,表从无到有。
做法: pom 加驱动/连接池 → yml 配 URL/用户/密码 → 建表可用 JPA ddl、SQL 脚本、Flyway/Liquibase;库本身通常需先手动创建(Boot 默认不建库,除非额外配置)。
Git、地图与其它工具
git stash → pull → stash pop
问题: 有本地未提交改动时 pull 失败/怕被覆盖。
做法: git stash → git pull(可用 --rebase)→ 冲突则处理并 rebase --continue → git stash pop。


Cursor 生成提交信息
问题:(备忘)用 AI 根据 diff 生成 commit message。
做法: 使用「生成提交信息」/@diff of working state 从当前差异生成。
API key 保护
问题: 密钥怎么存放才相对安全。
做法: 私密仓可写 config + 控制台限制;公开仓用 .env.*(示例用 .env.example)+ 限制;或接口下发 key;或 Vault。
Google 地图 API
问题:(备忘)地图相关 API 分工。
做法: Static(静态图)、Maps JavaScript(动态)、Places(搜索提示)、Geocoding(经纬度↔地址)。
防止浏览器缓存
问题: 页面/资源被旧缓存卡住。
做法: HTML meta 禁缓存;URL 加时间戳;服务端 Cache-Control 等响应头。
useRouter / useRoute / openAPI / byteMD
问题:(备忘)零散工具名。
说明: 源笔记一句话备忘,无展开。
做法: useRouter 跳转;useRoute 读当前路由;openAPI 按接口文档生成请求;byteMD 作 Markdown 相关能力。
在2c/4g上自建dify
问题:页面502
说明:全开满内存,需要关闭部分服务
做法:关闭weaviate服务,知识库检索功能无法使用或关闭其他服务来保证空余部分内存,Sandbox / plugin_daemon 不能长期关 — Code 节点与微信插件依赖它们。