diff --git a/common/js/http-error.js b/common/js/http-error.js
new file mode 100644
index 0000000..64506a2
--- /dev/null
+++ b/common/js/http-error.js
@@ -0,0 +1,101 @@
+/**
+ * 通用网络/接口错误分类器
+ *
+ * 适用场景:
+ * - uniapp 各端统一错误分类(diy.js / 商品 / 订单 / 自定义页面等)
+ * - 任意传入 {statusCode, errMsg, message} 形式错误对象的位置
+ *
+ * 设计要点:
+ * 1) 与 Vue / $lang 解耦:不调用任何 i18n 函数,只返回分类结果 + 原始错误字段
+ * 调用方拿到 type 后,自行决定使用 $lang('http.timeout') / $lang('http.serverError')
+ * 或其它命名空间的文案,完全保持各模块原有的 key 命名习惯
+ * 2) 通用网络/接口错误统一映射到 common.http.* 命名空间:
+ * - http.networkError: 通用网络异常(兜底)
+ * - http.timeout: 请求超时
+ * - http.serverError: 服务器错误(5xx)
+ * - http.notFound: 接口不存在(404)
+ * - http.auth: 无访问权限(401/403)
+ * - http.unknown: 其他非 2xx
+ * 3) 兼容多种错误对象形态:
+ * - uniapp request: { statusCode, errMsg, data }
+ * - axios: { response: { status }, message }
+ * - fetch / 原生: { message, name }
+ *
+ * @param {Object|string} err 错误对象或后端业务消息字符串
+ * @returns {{ type: string, statusCode?: number, errMsg?: string, raw?: any }}
+ * - type 取值:
+ * 'business' - 后端返回的业务消息字符串(已透传到 raw)
+ * 'empty' - 错误对象为空
+ * 'notFound' - HTTP 404
+ * 'server' - HTTP 5xx
+ * 'auth' - HTTP 401 / 403
+ * 'request' - 其它非 2xx 状态码
+ * 'timeout' - errMsg 含 timeout
+ * 'network' - errMsg 含 connect / network / fail
+ * 'unknown' - 无法分类
+ * - statusCode: 若有则原样返回
+ * - errMsg: 归一后的原始消息
+ * - raw: 原始输入,方便调用方进一步处理
+ */
+export function classifyHttpError(err) {
+ // 1) 无错误对象
+ if (!err) {
+ return { type: 'empty', raw: err };
+ }
+
+ // 2) 后端直接返回的业务消息(字符串)
+ if (typeof err === 'string') {
+ return { type: 'business', raw: err };
+ }
+
+ // 3) 字段归一(兼容 uni / axios / 原生 Error)
+ const statusCode = err.statusCode
+ ?? err.response?.status
+ ?? err.status;
+ const errMsg = err.errMsg || err.message || '';
+
+ if (statusCode) {
+ if (statusCode === 404) return { type: 'notFound', statusCode, errMsg, raw: err };
+ if (statusCode >= 500) return { type: 'server', statusCode, errMsg, raw: err };
+ if (statusCode === 401 || statusCode === 403) return { type: 'auth', statusCode, errMsg, raw: err };
+ return { type: 'request', statusCode, errMsg, raw: err };
+ }
+
+ if (/timeout/i.test(errMsg)) {
+ return { type: 'timeout', errMsg, raw: err };
+ }
+ if (/(connect|network|fail)/i.test(errMsg)) {
+ return { type: 'network', errMsg, raw: err };
+ }
+
+ return { type: 'unknown', errMsg, raw: err };
+}
+
+/**
+ * 根据 classifyHttpError 的结果,映射到 common.http.* 命名空间的 i18n key
+ *
+ * 通用网络/接口错误统一走 http.*,调用方通过 $lang(key) 解析文案。
+ * 原 diy.errTimeout 这类 key 已合并到 http.*,保持项目国际化更专业。
+ *
+ * @param {string} type classifyHttpError 返回的 type
+ * @returns {string|null} 对应的 http.* key;type='business' 时返回 null(透传 raw)
+ */
+export function httpErrorKey(type) {
+ switch (type) {
+ case 'empty': return 'http.networkError';
+ case 'business': return null; // 透传,不用 key
+ case 'notFound': return 'http.notFound';
+ case 'server': return 'http.serverError';
+ case 'auth': return 'http.auth';
+ case 'request': return 'http.unknown';
+ case 'timeout': return 'http.timeout';
+ case 'network': return 'http.networkError';
+ case 'unknown':
+ default: return 'http.networkError';
+ }
+}
+
+export default {
+ classifyHttpError,
+ httpErrorKey,
+};
\ No newline at end of file
diff --git a/diy-category-item-branch.vue b/diy-category-item-branch.vue
new file mode 100644
index 0000000..14d4690
--- /dev/null
+++ b/diy-category-item-branch.vue
@@ -0,0 +1,2522 @@
+
+
+
+
+
+
+
+
+
+
+ {{ one.category_name }}
+
+
+
+
+
+
+
+ {{ item.label_name }}
+
+
+
+ {{ isEnEnv ? item.en_goods_name : item.goods_name }}
+
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}
+ .{{ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}
+
+
+
+
+
+
+
+
+
+ {{ $lang('components-diy.diy-category.noCategory') }}
+
+
+
+
+
+
+ {{ one.category_name }}
+
+
+
+
+
+ {{ item.label_name }}
+
+
+
+ {{ isEnEnv ? item.en_goods_name : item.goods_name }}
+
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}
+ .{{ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}
+
+
+
+
+
+
+
+
+ 暂无商品
+
+
+
+
+
+
+
+ {{ category.category_name }}
+
+
+
+
+
+ {{ item.label_name }}
+
+
+
+ {{ isEnEnv ? item.en_goods_name : item.goods_name }}
+
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}
+ .{{ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}
+
+
+
+
+
+
+
+
+ 暂无商品
+
+
+
+
+
+
+ {{ category.category_name }}
+
+
+
+
+
+ {{ one.category_name }}
+
+
+
+
+
+
+ {{ one.category_name }}
+
+
+
+
+
+ {{ two.category_name }}
+
+
+
+
+
+
+
+
+ {{ category.category_name }}
+
+
+
+
+
+ {{ item.label_name }}
+
+
+
+ {{ isEnEnv ? item.en_goods_name : item.goods_name }}
+
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}
+ .{{ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}
+
+
+
+
+
+
+
+
+ 暂无商品
+
+
+
+
+
+
+
+
+
+
+ {{ $lang('components-diy.diy-category-item.all') }}
+
+ {{ one.category_name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ category.category_name }}
+
+
+
+
+
+
+
+
+ {{ item.label_name }}
+
+
+
+
+
+ {{ isEnEnv ? item.en_goods_name : item.goods_name }}
+
+
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{
+ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}
+ .{{
+ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{ showMarketPrice(item) }}
+
+
+
+ {{ $lang('components-diy.diy-category-item.buyNow') }}
+
+
+
+
+
+
+ {{ isEnEnv ? item.en_goods_name : item.goods_name }}
+
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{
+ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}
+ .{{
+ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}
+
+
+
+
+
+
+
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{ showMarketPrice(item) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label_name }}
+
+
+
+
+ {{ isEnEnv ? item.en_goods_name : item.goods_name }}
+
+
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{
+ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}
+ .{{
+ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{ showMarketPrice(item) }}
+
+
+
+ {{ $lang('components-diy.diy-category-item.buyNow') }}
+
+
+
+
+
+
+ {{ isEnEnv ? item.en_goods_name : item.goods_name }}
+
+
+
+
+ {{ $lang('common.currencySymbol') }}
+ {{
+ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}
+ .{{
+ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+