wuyunfeng 4 месяцев назад
Сommit
a8d840df33
100 измененных файлов с 22389 добавлено и 0 удалено
  1. 18 0
      .env.development
  2. 18 0
      .env.production
  3. 7 0
      .gitignore
  4. 10 0
      babel.config.js
  5. 60 0
      components.d.ts
  6. 31 0
      config/dev.ts
  7. 118 0
      config/index.ts
  8. 43 0
      config/prod.ts
  9. 91 0
      package.json
  10. 15688 0
      pnpm-lock.yaml
  11. 15 0
      project.config.json
  12. 13 0
      project.tt.json
  13. 39 0
      src/api/afterSale.ts
  14. 13 0
      src/api/article.ts
  15. 47 0
      src/api/billAccount.ts
  16. 12 0
      src/api/category.ts
  17. 31 0
      src/api/comment.ts
  18. 57 0
      src/api/cooperationShip.ts
  19. 10 0
      src/api/file.ts
  20. 76 0
      src/api/goods.ts
  21. 37 0
      src/api/member.ts
  22. 40 0
      src/api/mqttDevice.ts
  23. 103 0
      src/api/passport.ts
  24. 47 0
      src/api/receipt.ts
  25. 12 0
      src/api/regions.ts
  26. 65 0
      src/api/shop.ts
  27. 45 0
      src/api/shopcat.ts
  28. 65 0
      src/api/specification.ts
  29. 26 0
      src/api/staffBalance.ts
  30. 62 0
      src/api/statement.ts
  31. 17 0
      src/api/statistics.ts
  32. 24 0
      src/api/trade.ts
  33. 41 0
      src/api/withdraw.ts
  34. 89 0
      src/app.config.ts
  35. 14 0
      src/app.less
  36. 22 0
      src/app.ts
  37. 43 0
      src/assets/iconfont.css
  38. 1 0
      src/assets/iconfont.js
  39. 58 0
      src/assets/iconfont.json
  40. BIN
      src/assets/iconfont.ttf
  41. BIN
      src/assets/iconfont.woff
  42. BIN
      src/assets/iconfont.woff2
  43. 6 0
      src/custom-tab-bar/index.config.ts
  44. 93 0
      src/custom-tab-bar/index.vue
  45. 112 0
      src/hooks/after-sale/useAfterSaleHook.ts
  46. 41 0
      src/hooks/article/useArticleHook.ts
  47. 138 0
      src/hooks/bill-account-edit/useBillAccountEditHook.ts
  48. 102 0
      src/hooks/bill-account/useBillAccountHook.ts
  49. 221 0
      src/hooks/comment/useCommentHook.ts
  50. 232 0
      src/hooks/cooperation-ship-detail/useCooperationShipDetailHook.tsx
  51. 128 0
      src/hooks/cooperation-ship/useCooperationShipHook.tsx
  52. 55 0
      src/hooks/cooperation-ship/useCooperationShipSelectHook.ts
  53. 125 0
      src/hooks/mqtt-device/useMqttDeviceEditHook.ts
  54. 161 0
      src/hooks/mqtt-device/useMqttDeviceHook.ts
  55. 107 0
      src/hooks/order/useOrderHook.ts
  56. 145 0
      src/hooks/product-group/useProductGroupHook.tsx
  57. 83 0
      src/hooks/product-manager/useProductManagerHook.ts
  58. 94 0
      src/hooks/product-manager/useProductSearchHook.ts
  59. 72 0
      src/hooks/product-manager/useProductSkuQuantityHook.ts
  60. 589 0
      src/hooks/product-publish/useProductPublishHook.ts
  61. 139 0
      src/hooks/product-spec/useProductSpecAddHook.ts
  62. 149 0
      src/hooks/product-spec/useProductSpecHook.tsx
  63. 76 0
      src/hooks/product-spec/useSpecSearchHook.ts
  64. 148 0
      src/hooks/scan-receipt/useScanReceiptRecordHook.ts
  65. 255 0
      src/hooks/shop-apply/useShopApplyHook.ts
  66. 226 0
      src/hooks/shop-info/useShopInfoHook.tsx
  67. 74 0
      src/hooks/shop-time/useShopOpenTimeHook.tsx
  68. 169 0
      src/hooks/statement-detail/useStatementDetailHook.ts
  69. 165 0
      src/hooks/statement-query/useStatementQueryHook.ts
  70. 119 0
      src/hooks/statement/useGenerateStatementHook.ts
  71. 104 0
      src/hooks/statement/useStatementHook.tsx
  72. 284 0
      src/hooks/statistics/useStatisticsHook.ts
  73. 164 0
      src/hooks/user-info/useUserInfoHook.tsx
  74. 129 0
      src/hooks/withdraw/useWithdrawApplyHook.ts
  75. 136 0
      src/hooks/withdraw/useWithdrawHook.ts
  76. 34 0
      src/hooks/workbench/useShopStatisticHook.ts
  77. 65 0
      src/hooks/workbench/useWorkbenchHook.tsx
  78. 34 0
      src/hooks/workbench/useWorkbenchSwitchShopHook.ts
  79. 6 0
      src/http/baseUrl.ts
  80. 201 0
      src/http/request.ts
  81. BIN
      src/images/account-login.png
  82. BIN
      src/images/account.png
  83. BIN
      src/images/after-sale.png
  84. BIN
      src/images/bill.png
  85. BIN
      src/images/category.png
  86. BIN
      src/images/clerk.png
  87. BIN
      src/images/comment.png
  88. BIN
      src/images/cooperation.png
  89. BIN
      src/images/dashboard_bg.png
  90. BIN
      src/images/device.png
  91. BIN
      src/images/empty-status.png
  92. BIN
      src/images/fund.png
  93. BIN
      src/images/glass-add.png
  94. BIN
      src/images/glass-document.png
  95. BIN
      src/images/glass-lock.png
  96. BIN
      src/images/glass-paper.png
  97. BIN
      src/images/glass-profile.png
  98. BIN
      src/images/glass-setting.png
  99. BIN
      src/images/glass-shield.png
  100. 0 0
      src/images/glass-shops.png

+ 18 - 0
.env.development

@@ -0,0 +1,18 @@
+# 卖家端接口地址 
+TARO_APP_SELLER_URL='https://api.seller.wdklian.com' 
+# BASE接口地址 
+TARO_APP_BASE_URL='https://api.base.wdklian.com' 
+# 合作机构接口地址     
+TARO_APP_ORG_URL='https://api.org.wdklian.com' 
+# 买家端接口地址 
+TARO_APP_BUYER_URL='https://api.buyer.wdklian.com' 
+
+
+# // 卖家端接口地址 
+# TARO_APP_SELLER_URL='http://192.168.1.198:7003' 
+# // BASE接口地址 
+# TARO_APP_BASE_URL='http://192.168.1.198:7000' 
+# // 合作机构接口地址     
+# TARO_APP_ORG_URL='http://192.168.1.198:7001' 
+# // 买家端接口地址 
+# TARO_APP_BUYER_URL='http://192.168.1.198:7009' 

+ 18 - 0
.env.production

@@ -0,0 +1,18 @@
+# 卖家端接口地址 
+TARO_APP_SELLER_URL='https://api.seller.wdklian.com' 
+# BASE接口地址 
+TARO_APP_BASE_URL='https://api.base.wdklian.com' 
+# 合作机构接口地址     
+TARO_APP_ORG_URL='https://api.org.wdklian.com' 
+# 买家端接口地址 
+TARO_APP_BUYER_URL='https://api.buyer.wdklian.com' 
+
+
+# // 卖家端接口地址 
+# TARO_APP_SELLER_URL='http://192.168.1.198:7003' 
+# // BASE接口地址 
+# TARO_APP_BASE_URL='http://192.168.1.198:7000' 
+# // 合作机构接口地址     
+# TARO_APP_ORG_URL='http://192.168.1.198:7001' 
+# // 买家端接口地址 
+# TARO_APP_BUYER_URL='http://192.168.1.198:7009' 

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+dist/
+deploy_versions/
+.temp/
+.rn_temp/
+node_modules/
+.DS_Store
+.swc

+ 10 - 0
babel.config.js

@@ -0,0 +1,10 @@
+// babel-preset-taro 更多选项和默认值:
+// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
+module.exports = {
+  presets: [
+    ['taro', {
+      framework: 'vue3',
+      ts: true     
+    }]
+  ]
+}

+ 60 - 0
components.d.ts

@@ -0,0 +1,60 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+export {}
+
+declare module 'vue' {
+  export interface GlobalComponents {
+    NutActionSheet: typeof import('@nutui/nutui-taro')['ActionSheet']
+    NutAnimate: typeof import('@nutui/nutui-taro')['Animate']
+    NutAvatar: typeof import('@nutui/nutui-taro')['Avatar']
+    NutBadge: typeof import('@nutui/nutui-taro')['Badge']
+    NutButton: typeof import('@nutui/nutui-taro')['Button']
+    NutCalendar: typeof import('@nutui/nutui-taro')['Calendar']
+    NutCard: typeof import('@nutui/nutui-taro')['Card']
+    NutCascader: typeof import('@nutui/nutui-taro')['Cascader']
+    NutCell: typeof import('@nutui/nutui-taro')['Cell']
+    NutCellGroup: typeof import('@nutui/nutui-taro')['CellGroup']
+    NutCheckbox: typeof import('@nutui/nutui-taro')['Checkbox']
+    NutCheckboxGroup: typeof import('@nutui/nutui-taro')['CheckboxGroup']
+    NutCircleProgress: typeof import('@nutui/nutui-taro')['CircleProgress']
+    NutCol: typeof import('@nutui/nutui-taro')['Col']
+    NutComment: typeof import('@nutui/nutui-taro')['Comment']
+    NutCountdown: typeof import('@nutui/nutui-taro')['Countdown']
+    NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker']
+    NutDialog: typeof import('@nutui/nutui-taro')['Dialog']
+    NutDivider: typeof import('@nutui/nutui-taro')['Divider']
+    NutEcard: typeof import('@nutui/nutui-taro')['Ecard']
+    NutEmpty: typeof import('@nutui/nutui-taro')['Empty']
+    NutForm: typeof import('@nutui/nutui-taro')['Form']
+    NutFormItem: typeof import('@nutui/nutui-taro')['FormItem']
+    NutGrid: typeof import('@nutui/nutui-taro')['Grid']
+    NutGridItem: typeof import('@nutui/nutui-taro')['GridItem']
+    NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview']
+    NutInput: typeof import('@nutui/nutui-taro')['Input']
+    NutMenu: typeof import('@nutui/nutui-taro')['Menu']
+    NutMenuItem: typeof import('@nutui/nutui-taro')['MenuItem']
+    NutNavbar: typeof import('@nutui/nutui-taro')['Navbar']
+    NutPicker: typeof import('@nutui/nutui-taro')['Picker']
+    NutPopup: typeof import('@nutui/nutui-taro')['Popup']
+    NutPrice: typeof import('@nutui/nutui-taro')['Price']
+    NutRate: typeof import('@nutui/nutui-taro')['Rate']
+    NutRow: typeof import('@nutui/nutui-taro')['Row']
+    NutSearchbar: typeof import('@nutui/nutui-taro')['Searchbar']
+    NutSpace: typeof import('@nutui/nutui-taro')['Space']
+    NutSticky: typeof import('@nutui/nutui-taro')['Sticky']
+    NutSwipe: typeof import('@nutui/nutui-taro')['Swipe']
+    NutSwipeGroup: typeof import('@nutui/nutui-taro')['SwipeGroup']
+    NutSwitch: typeof import('@nutui/nutui-taro')['Switch']
+    NutTabbar: typeof import('@nutui/nutui-taro')['Tabbar']
+    NutTabbarItem: typeof import('@nutui/nutui-taro')['TabbarItem']
+    NutTabPane: typeof import('@nutui/nutui-taro')['TabPane']
+    NutTabs: typeof import('@nutui/nutui-taro')['Tabs']
+    NutTag: typeof import('@nutui/nutui-taro')['Tag']
+    NutTextarea: typeof import('@nutui/nutui-taro')['Textarea']
+    NutTrendArrow: typeof import('@nutui/nutui-taro')['TrendArrow']
+    NutUploader: typeof import('@nutui/nutui-taro')['Uploader']
+  }
+}

+ 31 - 0
config/dev.ts

@@ -0,0 +1,31 @@
+module.exports = {
+  env: {
+    NODE_ENV: '"development"'
+  },
+  defineConstants: {
+  },
+  mini: {
+    mini: {
+      webpackChain: (chain, webpack) => {
+        chain.merge({
+          plugin: {
+            install: {
+              plugin: require('terser-webpack-plugin'),
+              args: [
+                {
+                  terserOptions: {
+                    compress: true, // 默认使用terser压缩
+                    // mangle: false,
+                    keep_classnames: true, // 不改变class名称
+                    keep_fnames: true, // 不改变函数名称
+                  },
+                },
+              ],
+            },
+          },
+        })
+      },
+    },
+  },
+  h5: {}
+}

+ 118 - 0
config/index.ts

@@ -0,0 +1,118 @@
+import Components from 'unplugin-vue-components/webpack';
+import NutUIResolver from '@nutui/auto-import-resolver';
+const path = require('path');
+
+const config = {
+  projectName: 'newseller',
+  alias: {
+    '@': path.resolve(__dirname, '..', 'src'),
+  },
+  date: '2024-7-19',
+  designWidth (input) {
+    if (input?.file?.replace(/\\+/g, '/').indexOf('@nutui') > -1) {
+      return 375
+    }
+    return 750
+  },
+  deviceRatio: {
+    640: 2.34 / 2,
+    750: 1,
+    828: 1.81 / 2,
+    375: 2 / 1
+  },
+  sourceRoot: 'src',
+  outputRoot: 'dist',
+  plugins: ['@tarojs/plugin-html'],
+  defineConstants: {
+  },
+  copy: {
+    patterns: [
+    ],
+    options: {
+    }
+  },
+  framework: 'vue3',
+  compiler: {
+    type: 'webpack5',
+    prebundle: { enable: false },
+
+  },
+  sass:{
+    data: `@import "@nutui/nutui-taro/dist/styles/variables.scss";`
+  },
+  mini: {
+    miniCssExtractPluginOption: {
+      ignoreOrder: true,
+    },
+    webpackChain(chain) {
+      chain.plugin('unplugin-vue-components').use(Components({
+        resolvers: [
+          NutUIResolver({
+            importStyle: 'sass',
+            taro: true
+          })
+        ],
+      }))
+    },
+    optimizeMainPackage: {
+      enable:true
+    },
+    postcss: {
+      pxtransform: {
+        enable: true,
+        config: {
+          // selectorBlackList: ['nut-']
+        }
+      },
+      url: {
+        enable: true,
+        config: {
+          limit: 1024 // 设定转换尺寸上限
+        }
+      },
+      cssModules: {
+        enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
+        config: {
+          namingPattern: 'module', // 转换模式,取值为 global/module
+          generateScopedName: '[name]__[local]___[hash:base64:5]'
+        }
+      }
+    }
+  },
+  h5: {
+    webpackChain(chain) {
+      chain.plugin('unplugin-vue-components').use(Components({
+        resolvers: [
+          NutUIResolver({
+            importStyle: 'sass',
+            taro: true
+          })
+        ]
+      }))
+    },
+    publicPath: '/',
+    staticDirectory: 'static',
+    esnextModules: ['nutui-taro', 'icons-vue-taro'],
+    postcss: {
+      autoprefixer: {
+        enable: true,
+        config: {
+        }
+      },
+      cssModules: {
+        enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
+        config: {
+          namingPattern: 'module', // 转换模式,取值为 global/module
+          generateScopedName: '[name]__[local]___[hash:base64:5]'
+        }
+      }
+    }
+  }
+}
+
+module.exports = function (merge) {
+  if (process.env.NODE_ENV === 'development') {
+    return merge({}, config, require('./dev'))
+  }
+  return merge({}, config, require('./prod'))
+}

+ 43 - 0
config/prod.ts

@@ -0,0 +1,43 @@
+module.exports = {
+  env: {
+    NODE_ENV: '"production"'
+  },
+  defineConstants: {
+  },
+  mini: {
+    webpackChain(chain) {
+      chain.merge({
+        performance: { maxEntrypointSize: 10000000, maxAssetSize: 600000 },
+      })
+    },
+  },
+  h5: {
+    /**
+     * WebpackChain 插件配置
+     * @docs https://github.com/neutrinojs/webpack-chain
+     */
+    // webpackChain (chain) {
+    //   /**
+    //    * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。
+    //    * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer
+    //    */
+    //   chain.plugin('analyzer')
+    //     .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
+
+    //   /**
+    //    * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。
+    //    * @docs https://github.com/chrisvfritz/prerender-spa-plugin
+    //    */
+    //   const path = require('path')
+    //   const Prerender = require('prerender-spa-plugin')
+    //   const staticDir = path.join(__dirname, '..', 'dist')
+    //   chain
+    //     .plugin('prerender')
+    //     .use(new Prerender({
+    //       staticDir,
+    //       routes: [ '/pages/index/index' ],
+    //       postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') })
+    //     }))
+    // }
+  }
+}

+ 91 - 0
package.json

@@ -0,0 +1,91 @@
+{
+  "name": "newseller",
+  "version": "1.0.0",
+  "private": true,
+  "description": "卖家端taro新版",
+  "templateInfo": {
+    "name": "vue3-NutUI4",
+    "typescript": true,
+    "css": "Less",
+    "framework": "Vue3"
+  },
+  "scripts": {
+    "build:weapp": "taro build --type weapp",
+    "build:swan": "taro build --type swan",
+    "build:alipay": "taro build --type alipay",
+    "build:tt": "taro build --type tt",
+    "build:h5": "taro build --type h5",
+    "build:rn": "taro build --type rn",
+    "build:qq": "taro build --type qq",
+    "build:jd": "taro build --type jd",
+    "build:quickapp": "taro build --type quickapp",
+    "dev:weapp": "npm run build:weapp -- --watch",
+    "dev:swan": "npm run build:swan -- --watch",
+    "dev:alipay": "npm run build:alipay -- --watch",
+    "dev:tt": "npm run build:tt -- --watch",
+    "dev:h5": "npm run build:h5 -- --watch",
+    "dev:rn": "npm run build:rn -- --watch",
+    "dev:qq": "npm run build:qq -- --watch",
+    "dev:jd": "npm run build:jd -- --watch",
+    "dev:quickapp": "npm run build:quickapp -- --watch"
+  },
+  "browserslist": [
+    "last 3 versions",
+    "Android >= 4.1",
+    "ios >= 8"
+  ],
+  "author": "",
+  "dependencies": {
+    "@babel/runtime": "^7.7.7",
+    "@nutui/icons-vue-taro": "^0.0.9",
+    "@nutui/nutui-taro": "^4.3.0",
+    "@tarojs/components": "3.6.34",
+    "@tarojs/helper": "3.6.34",
+    "@tarojs/plugin-framework-vue3": "3.6.34",
+    "@tarojs/plugin-html": "3.6.34",
+    "@tarojs/plugin-platform-alipay": "3.6.34",
+    "@tarojs/plugin-platform-h5": "3.6.34",
+    "@tarojs/plugin-platform-jd": "3.6.34",
+    "@tarojs/plugin-platform-qq": "3.6.34",
+    "@tarojs/plugin-platform-swan": "3.6.34",
+    "@tarojs/plugin-platform-tt": "3.6.34",
+    "@tarojs/plugin-platform-weapp": "3.6.34",
+    "@tarojs/runtime": "3.6.34",
+    "@tarojs/shared": "3.6.34",
+    "@tarojs/taro": "3.6.34",
+    "crypto-js": "^4.2.0",
+    "dayjs": "^1.11.12",
+    "js-md5": "^0.8.3",
+    "nanoid": "^5.0.7",
+    "pinia": "^2.1.7",
+    "postcss-loader": "^8.1.1",
+    "qs": "^6.13.0",
+    "vue": "^3.2.40"
+  },
+  "devDependencies": {
+    "@babel/core": "^7.8.0",
+    "@nutui/auto-import-resolver": "^1.0.0",
+    "@tarojs/cli": "3.6.34",
+    "@tarojs/taro-loader": "3.6.34",
+    "@tarojs/webpack5-runner": "3.6.34",
+    "@types/node": "^18.15.11",
+    "@types/webpack-env": "^1.13.6",
+    "@typescript-eslint/eslint-plugin": "^5.20.0",
+    "@typescript-eslint/parser": "^5.20.0",
+    "@vue/babel-plugin-jsx": "^1.0.6",
+    "@vue/compiler-sfc": "^3.2.40",
+    "babel-preset-taro": "3.6.34",
+    "css-loader": "3.4.2",
+    "eslint": "^8.12.0",
+    "eslint-config-taro": "3.6.34",
+    "eslint-plugin-vue": "^8.0.0",
+    "postcss": "^8.4.39",
+    "style-loader": "1.3.0",
+    "stylelint": "9.3.0",
+    "ts-node": "^10.9.1",
+    "typescript": "^4.1.0",
+    "unplugin-vue-components": "^0.26.0",
+    "vue-loader": "^17.0.0",
+    "webpack": "^5.78.0"
+  }
+}

Разница между файлами не показана из-за своего большого размера
+ 15688 - 0
pnpm-lock.yaml


+ 15 - 0
project.config.json

@@ -0,0 +1,15 @@
+{
+  "miniprogramRoot": "./dist",
+  "projectname": "newseller",
+  "description": "买家端taro新版",
+  "appid": "wxe95ca6dc3c58aed4",
+  "setting": {
+    "urlCheck": false,
+    "es6": false,
+    "enhance": false,
+    "compileHotReLoad": false,
+    "postcss": false,
+    "minified": false
+  },
+  "compileType": "miniprogram"
+}

+ 13 - 0
project.tt.json

@@ -0,0 +1,13 @@
+{
+  "miniprogramRoot": "./",
+  "projectname": "newseller",
+  "description": "买家端taro新版",
+  "appid": "touristappid",
+  "setting": {
+    "urlCheck": true,
+    "es6": false,
+    "postcss": false,
+    "minified": false
+  },
+  "compileType": "miniprogram"
+}

+ 39 - 0
src/api/afterSale.ts

@@ -0,0 +1,39 @@
+import request from "@/http/request";
+import {PageData, RefundDetailDTO, RefundQueryParamVO, SellerRefundApprovalVO} from "@/types";
+
+// 获取售后列表
+export const getAfterSaleList = (params: RefundQueryParamVO) => {
+    return request<PageData<RefundDetailDTO>>({
+        url: `/seller/after-sales/refundetails`,
+        method: 'GET',
+        data:params
+    })
+}
+
+
+// 获取售后详情
+export const getAfterSaleDetail = (sn: string) => {
+    return request<RefundDetailDTO>({
+        url: `/seller/after-sales/refunds/${sn}`,
+        method: 'GET'
+    })
+}
+
+//卖家退款
+export const sellerRefund = (sn: string, data: { remark?:string }) => {
+    return request({
+        url: `/seller/after-sales/refunds/${sn}`,
+        method: 'POST',
+        data
+    })
+}
+
+//卖家审核退款
+export const sellerAuditRefund = (sn: string, data: SellerRefundApprovalVO) => {
+    return request<SellerRefundApprovalVO>({
+        url: `/seller/after-sales/audits/${sn}`,
+        method: 'POST',
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        data
+    })
+}

+ 13 - 0
src/api/article.ts

@@ -0,0 +1,13 @@
+import request from "@/http/request";
+import {baseBaseUrl} from "@/http/baseUrl";
+import type {EsArticle} from "@/types";
+
+//通过Id获取文章
+
+export function getArticleById(id: number) {
+    return request<EsArticle>({
+        url: `/pages/articles/${id}`,
+        method: 'GET',
+        baseURL: baseBaseUrl,
+    })
+}

+ 47 - 0
src/api/billAccount.ts

@@ -0,0 +1,47 @@
+import request from "@/http/request";
+import type {CommonPageParam, PageData, RebateAccountDO} from "@/types";
+
+//获取提现账户列表
+export const getBillAccountList = (data:CommonPageParam) => {
+    return request<PageData<RebateAccountDO>>({
+        url: `/care/seller/rebate/rebate_account/page`,
+        method: 'POST',
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        data
+    })
+}
+
+//新增提现账户
+export const addBillAccount = (data: RebateAccountDO) => {
+    return request<RebateAccountDO>({
+        url: `/care/seller/rebate/rebate_account`,
+        method: 'POST',
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        data
+    })
+}
+
+//删除提现账户
+export const deleteBillAccount = (id: number) => {
+    return request<string>({
+        url: `/care/seller/rebate/rebate_account/${id}`,
+        method: 'DELETE'
+    })
+}
+
+//修改提现账户
+export const updateBillAccount = (id: number, data: RebateAccountDO) => {
+    return request<RebateAccountDO>({
+        url: `/care/seller/rebate/rebate_account/${id}`,
+        method: 'PUT',
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        data
+    })
+}
+//通过id获取提现账户
+export const getBillAccountById = (id: number) => {
+    return request<RebateAccountDO>({
+        url: `/care/seller/rebate/rebate_account/${id}`,
+        method: 'GET'
+    })
+}

+ 12 - 0
src/api/category.ts

@@ -0,0 +1,12 @@
+import request from "@/http/request";
+import {type Category} from "@/types";
+
+
+//获取类目列表
+export const getCategoryList = (id:number) => {
+    return request<Category[]>({
+        url: `/seller/goods/category/${id}`,
+        method: 'GET',
+        showLoading: false
+    })
+}

+ 31 - 0
src/api/comment.ts

@@ -0,0 +1,31 @@
+import request from "@/http/request";
+import type {CommentVO, CommonPageParam, PageData} from "@/types";
+
+//分页查询评论列表
+export function getCommentList(data: CommonPageParam) {
+    return request<PageData<CommentVO>>({
+        url: "/seller/members/comments",
+        method: "GET",
+        data
+    });
+}
+
+//回复评论
+export function replyComment(comment_id: number, data: { reply: string }) {
+    return request({
+        url: `/seller/members/comments/${comment_id}/reply`,
+        method: "POST",
+        header: {
+            'content-type': 'application/x-www-form-urlencoded'
+        },
+        hideLoading: true,
+        data
+    });
+}
+//查询评论总人数
+export function getCommentCount() {
+    return request<number>({
+        url: "/seller/members/comments/count",
+        method: "GET"
+    });
+}

+ 57 - 0
src/api/cooperationShip.ts

@@ -0,0 +1,57 @@
+import request from "@/http/request";
+import type {CooperationVO, PageData} from "@/types";
+//查询合作关系列表
+export const getCooperationList = (data) => {
+    return request<PageData<CooperationVO>>({
+        url: `/seller/cooperation-ship/page`,
+        method: 'POST',
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data
+    })
+}
+//新增合作关系
+export const addCooperation = (data:CooperationVO) => {
+    return request<CooperationVO>({
+        url: `/seller/cooperation-ship`,
+        method: 'POST',
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data
+    })
+}
+
+//编辑合作关系
+export const updateCooperation = (data:CooperationVO,id:number) => {
+    return request({
+        url: `/seller/cooperation-ship/${id}`,
+        method: 'PUT',
+        data
+    })
+}
+
+//查询合作关系详情
+export const getCooperationDetail = (id:number) => {
+    return request<CooperationVO>({
+        url: `/seller/cooperation-ship/${id}`,
+        method: 'GET'
+    })
+}
+
+//处理合作关系状态
+export const handleCooperationStatus = (id:number,status:number) => {
+    return request<CooperationVO>({
+        url: `/seller/cooperation-ship/deal-status/${id}/${status}`,
+        method: 'PUT'
+    })
+}
+
+//删除合作关系
+export const deleteCooperationShip = (id:number) => {
+    return request({
+        url: `/seller/cooperation-ship/${id}`,
+        method: 'DELETE'
+    })
+}

+ 10 - 0
src/api/file.ts

@@ -0,0 +1,10 @@
+import Taro from "@tarojs/taro";
+import {baseBaseUrl} from "@/http/baseUrl";
+
+export const uploadFile = ({filePath,scene}) => {
+    return  Taro.uploadFile({
+        url: `${baseBaseUrl}/uploaders?scene=${scene}`,
+        filePath,
+        name: 'file',
+    })
+}

+ 76 - 0
src/api/goods.ts

@@ -0,0 +1,76 @@
+import request from "@/http/request";
+import {
+    type GoodsDTO,
+    type GoodsQuantityDTO,
+    type GoodsQueryParam,
+    type GoodsVO,
+    type SkuEditItem,
+    type PageData
+} from "@/types";
+
+//查询商品列表
+export const getGoodsList = (data: GoodsQueryParam) => {
+    return request<PageData<GoodsVO>>({
+        url: `/seller/goods/v2`,
+        method: "GET",
+        showLoading: false,
+        data
+    });
+};
+//查询商品详情
+export const getGoodsDetail = (goodsId: number) => {
+    return request<GoodsVO>({
+        url: `/seller/goods/${goodsId}`,
+        method: "GET",
+        showLoading: false
+    });
+};
+
+//查询商品SKU信息
+export const getGoodsSku = (goodsId: number) => {
+    return request<SkuEditItem[]>({
+        url: `/seller/goods/${goodsId}/skus`,
+        method: "GET",
+        showLoading: false
+    });
+};
+
+//添加商品
+export const addGoods = (data: GoodsDTO) => {
+    return request<GoodsVO>({
+        url: `/seller/goods`,
+        method: "POST",
+        data
+    });
+};
+
+//修改商品
+export const updateGoods = (data: GoodsDTO) => {
+    return request<GoodsVO>({
+        url: `/seller/goods/${data.goods_id}`,
+        method: "PUT",
+        data
+    });
+};
+//商品上架
+export const putOnGoods = (goodsId: number) => {
+    return request({
+        url: `/seller/goods/${goodsId}/up`,
+        method: "PUT"
+    });
+};
+//商品下架
+export const putOffGoods = (goodsId: number) => {
+    return request({
+        url: `/seller/goods/${goodsId}/under`,
+        method: "PUT"
+    });
+};
+//更新库存
+export const updateSkuQuantity = (data: GoodsQuantityDTO[], goods_id: number) => {
+    return request({
+        url: `/seller/goods/${goods_id}/quantity`,
+        method: "PUT",
+        data
+    });
+};

+ 37 - 0
src/api/member.ts

@@ -0,0 +1,37 @@
+
+
+import request from "@/http/request";
+import {md5} from "js-md5";
+import {type MemberInfo} from "@/types";
+//获取用户信息
+export const getMemberInfo = () => {
+    return request<MemberInfo>({
+        url: `/seller/members`,
+        method: "GET",
+        showLoading: false
+    });
+};
+//更新用户信息
+export const updateMemberInfo = (data) => {
+    return request<MemberInfo>({
+        url: `/seller/members`,
+        method: "PUT",
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        showLoading: false,
+        data
+    });
+}
+
+//修改密码
+export const updatePassword = (data) => {
+    if(data.password){
+        data.password=md5(data.password)
+    }
+    return request({
+        url: `/seller/members/updatePwd`,
+        method: "POST",
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        showLoading: false,
+        data
+    });
+}

+ 40 - 0
src/api/mqttDevice.ts

@@ -0,0 +1,40 @@
+import request from "@/http/request";
+import type { MqttDevice,CommonPageParam, PageData, JsonResponse } from "@/types";
+
+//分页查询设备
+export const getMqttDeviceList = (data:CommonPageParam) => {
+    return request<PageData<MqttDevice>>({
+        url: '/seller/shop_mqtt_device/page',
+        method: 'POST',
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        data
+    })
+}
+
+//新增设备
+export const addMqttDevice = (data:MqttDevice) => {
+    return request<JsonResponse<MqttDevice>>({
+        url: '/seller/shop_mqtt_device',
+        method: 'POST',
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        data
+    })
+}
+
+//修改设备
+export const updateMqttDevice = (data:MqttDevice,id:number) => {
+    return request<MqttDevice>({
+        url: `/seller/shop_mqtt_device/${id}`,
+        method: 'PUT',
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        data
+    })
+}
+
+//删除设备
+export const deleteMqttDevice = (id:number) => {
+    return request<string>({
+        url: `/seller/shop_mqtt_device/${id}`,
+        method: 'DELETE'
+    })
+}

+ 103 - 0
src/api/passport.ts

@@ -0,0 +1,103 @@
+import request from "@/http/request";
+import {md5} from "js-md5";
+import {type MemberVo} from "@/types";
+//账号密码登陆
+export const getLogin = (params) => {
+    params.password = md5(params.password as string);
+    return request<MemberVo>({
+        url: `/passport/mini-program/seller-login/v2`,
+        method: "GET",
+        data: params,
+    });
+};
+//短信验证码登陆
+export const  smsLogin =(params) =>{
+    return request<MemberVo>({
+        url: `/passport/mini-program/sms-code-login/v2`,
+        method: 'get',
+        showLoading: false,
+        data:params
+    })
+}
+//退出登陆
+export const logout =(data)=>{
+  return request({
+      url:`/passport/mini-program/logout`,
+      method:'POST',
+      showLoading: false,
+      data
+  })
+}
+//发送登陆验证码
+export const sendLoginSmsCode =(mobile)=>{
+    return request({
+        url:`/passport/mini-program/smslogin/${mobile}`,
+        method:'POST',
+        showLoading: false
+    })
+}
+//解密用户信息
+export const accessUnionID = (params) => {
+    return request({
+        url:`/passport/mini-program/decryptUserinfo`,
+        method:'GET',
+        showLoading: false,
+        data:params
+    })
+}
+
+export const autoLogin = (params) => {
+    return request({
+        url: `/passport/mini-program/auto-login`,
+        method: 'GET',
+        showLoading: false,
+        data: params,
+    });
+}
+//缓存微信用户登陆信息
+export const cacheWechatInfo = (params) => {
+    return request({
+        url: `/passport/mini-program/cache-wechat-info`,
+        method: 'GET',
+        showLoading: false,
+        data: params,
+    });
+}
+
+//获取微信验证手机号
+export const getWechatPhoneNumber=(params)=>{
+    return request<any>({
+        url:`/passport/mini-program/get-wechat-phone-number`,
+        method:'GET',
+        showLoading: false,
+        data:params
+    })
+}
+
+//切换登陆店铺
+export const switchShop =(shopId)=>{
+    return request<MemberVo>({
+        url:`/seller/login/changeLoginShop/${shopId}`,
+        method:'GET',
+        showLoading: false
+    })
+}
+//通过memberId登陆
+export const loginByMemberId =(memberId)=>{
+    return request<MemberVo>({
+        url:`/passport/mini-program/buyer-to-seller`,
+        method:'GET',
+        showLoading: false,
+        data:{memberId}
+    })
+}
+
+//通过手机号登陆
+export const loginByMobile = (mobile) => {
+    return request<MemberVo>({
+        url: `/passport/mini-program/wechat-mobile-login`,
+        method: 'GET',
+        showLoading: false,
+        data: { mobile }
+    })
+}

+ 47 - 0
src/api/receipt.ts

@@ -0,0 +1,47 @@
+import request from "@/http/request";
+import type { PageData, PaymentBillDO, ScanReceiptDailyCountVO,  PaymentBillParam,  ScanReceiptParams } from "@/types";
+
+//扫码收款
+export const scanReceipt = (data: ScanReceiptParams) => {
+    return request({
+        url: `/seller/receipt/scaner-receipt`,
+        method: 'POST',
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data
+    })
+}
+
+// 扫码收款记录
+export const scanReceiptRecord = (data: PaymentBillParam) => {
+    return request<PageData<PaymentBillDO>>({
+        url: `/seller/receipt/page`,
+        method: 'POST',
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data
+    })
+}
+
+// 扫码收款每日统计
+export const scanReceiptDailyCount = (data: {create_time?: number,seller_id?: number}) => {
+    return request<ScanReceiptDailyCountVO>({
+        url: `/seller/receipt/scaner-receipt/daily-count`,
+        method: 'GET',
+        data
+    })
+}
+
+// 扫码收款退款
+export const scanReceiptRefund = (billId: number) => {
+    return request({
+        url: `/seller/receipt/scaner-receipt/refund`,
+        method: 'POST',
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data: {payment_bill_id: billId}
+    })
+}

+ 12 - 0
src/api/regions.ts

@@ -0,0 +1,12 @@
+import request from "@/http/request";
+import {baseBaseUrl} from "@/http/baseUrl";
+import {type Region} from "@/types/base";
+
+export const getChildRegions = (id) => {
+    return request<Region[]>({
+        url: `/regions/${id}/children`,
+        baseURL: baseBaseUrl,
+        showLoading: false,
+        method: "GET"
+    });
+};

+ 65 - 0
src/api/shop.ts

@@ -0,0 +1,65 @@
+import { buyerBaseUrl } from "@/http/baseUrl";
+import request from "@/http/request";
+import type {O2OShopVO,  Shop,  ShopStatistics} from "@/types";
+//获取店铺列表
+export const getShopList = (data: any) => {
+    return request<Shop[]>({
+        url: `/seller/shops/getSellerShops`,
+        method: 'GET',
+        showLoading: false,
+        data
+    })
+}
+//更新店铺状态
+export const updateShopStatus = (data: any) => {
+    return request({
+        url: `/seller/shops/updateShopState`,
+        method: 'PUT',
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data
+    })
+}
+
+//更新店铺信息
+export const updateShopInfo = (data: any) => {
+    return request({
+        url: `/seller/shops`,
+        method: 'PUT',
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data
+    })
+}
+//获取店铺统计信息
+export const getShopStatistic = () => {
+    return request<ShopStatistics>({
+        url: `/seller/statistics/dashboard/shop`,
+        method: 'GET'
+    })
+}
+//卖家申请新店铺
+export const sellerApplyNewShop = (data: O2OShopVO) => {
+    return request<Shop>({
+        url: `/seller/shops/o2o/apply`,
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        method: 'POST',
+        data
+    })
+}
+//买家申请新店铺
+export const buyerApplyNewShop = (data: O2OShopVO) => {
+    return request<Shop>({
+        baseURL:buyerBaseUrl ,
+        url: `/shops/o2o/apply`,
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        method: 'POST',
+        data
+    })
+}

+ 45 - 0
src/api/shopcat.ts

@@ -0,0 +1,45 @@
+import request from "@/http/request";
+import {ProductGroup} from "@/types";
+//获取店铺分组列表
+export const getShopCatList = () => {
+    return request<ProductGroup[]>({
+        url: `/seller/shops/cats/goods-count`,
+        method: 'GET',
+        showLoading: false
+    })
+}
+
+//添加店铺分组
+export const addShopCat = (data: any) => {
+    return request<ProductGroup>({
+        url: `/seller/shops/cats`,
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        method: 'POST',
+        data
+    })
+}
+
+//更新店铺分组
+export const updateShopCat = (data: any,id:number) => {
+    return request<ProductGroup>({
+        url: `/seller/shops/cats/${id}`,
+        method: 'PUT',
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data
+    })
+}
+
+//删除店铺分组
+export const deleteShopCat = (id: number) => {
+    return request<String>({
+        url: `/seller/shops/cats/${id}`,
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        method: 'DELETE'
+    })
+}

+ 65 - 0
src/api/specification.ts

@@ -0,0 +1,65 @@
+import request from "@/http/request";
+import {Specification} from "@/types";
+
+//查询商家自定义规格
+export const getSpecificationList = (data: any) => {
+    return request<Specification[]>({
+        url: `/seller/goods/categories/specs`,
+        method: 'POST',
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        data
+    })
+}
+//添加商家自定义规格
+export const addSpecification = (data: any,categoryId:number) => {
+    return request({
+        url: `/seller/goods/categories/${categoryId}/specs`,
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        method: 'POST',
+        data
+    })
+}
+
+//添加商家自定义规格值
+export const addSpecificationValue = (data: any,spec_id:number) => {
+    return request({
+        url: `/seller/goods/specs/${spec_id}/values`,
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        method: 'POST',
+        data
+    })
+}
+
+//修改商家自定义规格值
+export const updateSpecificationValue = (data: any,spec_value_id:number) => {
+    return request({
+        url: `/seller/goods/specs/values/${spec_value_id}`,
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        method: 'PUT',
+        data
+    })
+}
+//删除商家自定义规格值
+export const deleteSpecificationValue = (spec_value_id:number) => {
+    return request({
+        url: `/seller/goods/specs/values/${spec_value_id}`,
+        method: 'DELETE',
+    })
+}
+
+//修改商家自定义规格
+export const updateSpecification = (data: any,spec_id:number) => {
+    return request({
+        url: `/seller/goods/categories/specs/${spec_id}`,
+        header: {"Content-Type": "application/x-www-form-urlencoded"},
+        method: 'PUT',
+        data
+    })
+}
+//删除商家自定义规格
+export const deleteSpecification = (spec_id:number) => {
+    return request({
+        url: `/seller/goods/categories/specs/${spec_id}`,
+        method: 'DELETE',
+    })
+}

+ 26 - 0
src/api/staffBalance.ts

@@ -0,0 +1,26 @@
+import request from "@/http/request";
+import type {PageData,StaffBalanceDetail, StaffBalanceDetailQueryParam} from "@/types";
+
+//获取员工余额使用明细
+export function getStaffBalanceDetail(data:StaffBalanceDetailQueryParam) {
+    return request<PageData<StaffBalanceDetail>>({
+        url: '/seller/staff-balance-detail/page',
+        method: 'POST',
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data
+    });
+}
+
+//获取员工余额使用总额
+export function getStaffBalanceTotal(data:StaffBalanceDetailQueryParam) {
+    return request<number>({
+        url: '/seller/staff-balance-detail/amount',
+        method: 'POST',
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data
+    });
+}

+ 62 - 0
src/api/statement.ts

@@ -0,0 +1,62 @@
+import request from "@/http/request";
+import type {PageData, StaffBalanceDetailQueryParam, StatementItemQueryParam, StatementItemVO, StatementQueryParam, StatementVO} from "@/types";
+
+//查询账单列表
+export function getStatementList(data: StatementQueryParam) {
+    return request<PageData<StatementVO>>({
+        url: "/seller/statement/page",
+        method: "POST",
+        header: {
+            'content-type': 'application/x-www-form-urlencoded'
+        },
+        data
+    });
+}
+
+//查询账单详情
+export function getStatementDetail(id: number) {
+    return request<StatementVO>({
+        url: `/seller/statement/${id}`,
+        method: "GET",
+    });
+}
+
+//修改账单
+export function updateStatement(id: number, data: StatementVO) {
+    return request({
+        url: `/seller/statement/${id}`,
+        method: "PUT",
+        data
+    });
+}
+
+//查询账单条目
+export function getStatementItem(data: StatementItemQueryParam) {
+    return request<PageData<StatementItemVO>>({
+        url: `/seller/statement/item/page`,
+        method: "POST",
+        header: {
+            'content-type': 'application/x-www-form-urlencoded'
+        },
+        data
+    });
+}
+//处理账单状态
+export function handleStatementStatus(id: number, status: number) {
+    return request({
+        url: `/seller/statement/deal-status/${id}/${status}`,
+        method: "POST",
+    });
+}
+
+// 生成账单
+export function addStatement(data: StaffBalanceDetailQueryParam,cooperationId:number) {
+    return request({
+        url: `/seller/statement/add/${cooperationId}`,
+        method: "POST",
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data
+    });
+}

+ 17 - 0
src/api/statistics.ts

@@ -0,0 +1,17 @@
+import request from '@/http/request';
+import type { StatisticsData } from '@/types/statistics';
+
+/**
+ * 获取统计报表数据
+ * @returns Promise<StatisticsData> 统计数据
+ */
+export function getStatisticsReport(year: number, month: number){
+  return request<StatisticsData>({
+    url: '/seller/statistics/reports/mp_data',
+    method: 'get',
+    data: {
+      year,
+      month
+    }
+  });
+}

+ 24 - 0
src/api/trade.ts

@@ -0,0 +1,24 @@
+import request from "@/http/request";
+import type {PageData,TradeVO} from "@/types";
+
+
+//获取交易列表
+export const getTradeList = (data: any) => {
+    return request<PageData<TradeVO>>({
+        url: `/seller/trade/orders`,
+        method: 'GET',
+        showLoading: false,
+        data
+    })
+}
+//订单发货
+export const deliveryGoods = (sn:string,data: any) => {
+    return request({
+        url: `/seller/trade/orders/${sn}/delivery`,
+        method: 'POST',
+        header: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+        },
+        data
+    })
+}

+ 41 - 0
src/api/withdraw.ts

@@ -0,0 +1,41 @@
+import request from "@/http/request";
+import {CommonPageParam, PageData, RebateShopVO, RebateWithdrawApplyDO} from "@/types";
+
+//提现列表
+export function getWithdrawList(data: CommonPageParam) {
+    return request<PageData<RebateWithdrawApplyDO>>({
+        url: `/care/shopWithdraw/page`,
+        method: 'GET',
+        header: {
+            'content-type': 'application/x-www-form-urlencoded'
+        },
+        data
+    })
+}
+//提现详情
+export function getWithdrawDetail(id: number) {
+    return request<RebateWithdrawApplyDO>({
+        url: `/care/shopWithdraw/${id}`,
+        method: 'GET'
+    })
+}
+
+//获取账户余额信息
+export function getAccountBalance(shop_id: number) {
+    return request<RebateShopVO>({
+        url: `/care/shopWithdraw/rebateshop/${shop_id}`,
+        method: 'GET'
+    })
+}
+
+//新增提现申请
+export function addWithdraw(data: RebateWithdrawApplyDO) {
+    return request<RebateWithdrawApplyDO>({
+        url: `/care/shopWithdraw/apply-withdraw`,
+        method: 'POST',
+        header: {
+            'content-type': 'application/x-www-form-urlencoded'
+        },
+        data
+    })
+}

+ 89 - 0
src/app.config.ts

@@ -0,0 +1,89 @@
+export default defineAppConfig({
+    pages: [
+        'pages/workbench/index',
+        'pages/order/index',       
+        'pages/my/index',
+        'pages/login/index',
+        'pages/index/index'
+    ],
+    window: {
+        backgroundTextStyle: 'light',
+        navigationBarBackgroundColor: '#ffffff',
+        navigationBarTitleText: 'WeChat',
+        navigationBarTextStyle: 'black'
+    },
+    tabBar: {
+        custom: true,
+        color: '#333',
+        selectedColor: '#6190E8',
+        backgroundColor: '#fff',
+        borderStyle: 'black',
+        list: [
+            {
+                pagePath: 'pages/order/index',
+                selectedIconPath: 'images/tabbar_order_on.png',
+                iconPath: 'images/tabbar_order.png',
+                text: '订单'
+            },
+            {
+                pagePath: 'pages/workbench/index',
+                selectedIconPath: 'images/tabbar_cate_on.png',
+                iconPath: 'images/tabbar_cate.png',
+                text: '工作台'
+            },
+            {
+                pagePath: 'pages/my/index',
+                selectedIconPath: 'images/tabbar_my_on.png',
+                iconPath: 'images/tabbar_my.png',
+                text: '个人中心'
+            }]
+    },
+    subpackages: [
+        {
+            root: 'subPackageA',
+            pages: [
+                'pages/shop-info/index', //当前登陆的店铺信息
+                'pages/shop-open-time/index', //营业时间
+                'pages/shop-list/index', //店铺列表
+                'pages/apply-new-shop/index', //申请新店铺
+                'pages/clerk-manager/index', //店员管理
+                'pages/user-info/index', //账号设置
+                'pages/user-info-nickname-edit/index', //验证手机号
+                'pages/password-security/index', //密码安全
+                'pages/product-group/index', //商品分组
+                'pages/product-spec/index', //商品规格
+                'pages/product-spec-add/index', //新增商品规格
+                'pages/product-manager/index', //商品管理
+                'pages/product-edit/index', //商品编辑
+                'pages/product-publish/index', //商品发布
+                'pages/shop-devices/index', //店铺设备
+                'pages/shop-device-edit/index', //添加设备
+                'pages/bill-account/index', //结算账户
+                'pages/bill-account-edit/index', //新增结算账户
+                'pages/withdraw-apply/index', //提现申请
+                'pages/withdraw-apply-add/index', //申请提现
+                
+            ]
+        },
+        {
+            root: 'subPackageB',
+            pages: [
+                'pages/cooperation-ship/index', //合作关系列表
+                'pages/cooperation-ship-detail/index', //合作关系详情
+                'pages/trade/index', //订单管理
+                'pages/statement/index', //对账单,
+                'pages/statement-detail/index', //账单详情
+                'pages/statement-items/index', //账单明细
+                'pages/statement-query/index', //结算查询
+                'pages/generate-statement/index', //对账单生成
+                'pages/after-sale/index', //售后订单
+                'pages/comments/index', //评价管理
+                'pages/statistics/index', //销售统计
+                'pages/article/index', //文章管理
+                'pages/scan-receipt/index', //扫码收款
+                'pages/scan-receipt-record/index', //扫码收款记录
+            ]
+        }
+    ],
+    __usePrivacyCheck__: true
+})

+ 14 - 0
src/app.less

@@ -0,0 +1,14 @@
+
+Page{
+    background-color: #f7f8fa;
+    font-size: 24px;
+    color: var(--nut-cell-color, var(--nut-title-color2, #666666));
+}
+.main-container{
+  padding: 20px;
+  padding-bottom: env(safe-area-inset-bottom); // 适配iPhone X底部安全区域
+}
+
+.nut-cell__title{
+  justify-content: center;
+}

+ 22 - 0
src/app.ts

@@ -0,0 +1,22 @@
+import { createApp } from 'vue'
+import {setupStore} from "./store";
+import './app.less'
+import './assets/iconfont.css'
+import {useUuidStoreHook} from "@/store/uuidStore";
+const App = createApp({
+  onShow (options) {
+    console.log('__show',options)
+  },
+  // 对应 onLaunch
+  onLaunch () {
+    //置入UUID
+    useUuidStoreHook().setUuid()
+   
+
+  }
+  // 入口组件不需要实现 render 方法,即使实现了也会被 taro 所覆盖
+})
+setupStore(App);
+
+
+export default App

+ 43 - 0
src/assets/iconfont.css

@@ -0,0 +1,43 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 4627894 */
+  src: url('iconfont.woff2?t=1721901402494') format('woff2'),
+       url('iconfont.woff?t=1721901402494') format('woff'),
+       url('iconfont.ttf?t=1721901402494') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-yonghuming:before {
+  content: "\e616";
+}
+
+.icon-mima:before {
+  content: "\e652";
+}
+
+.icon-shouji:before {
+  content: "\e853";
+}
+
+.icon-yanzhengma:before {
+  content: "\e634";
+}
+
+.icon-wode:before {
+  content: "\e6ae";
+}
+
+.icon-order:before {
+  content: "\e682";
+}
+
+.icon-gongzuotai1:before {
+  content: "\e601";
+}
+

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
src/assets/iconfont.js


+ 58 - 0
src/assets/iconfont.json

@@ -0,0 +1,58 @@
+{
+  "id": "4627894",
+  "name": "new_seller",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon-",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "1604780",
+      "name": "用户名",
+      "font_class": "yonghuming",
+      "unicode": "e616",
+      "unicode_decimal": 58902
+    },
+    {
+      "icon_id": "2678617",
+      "name": "密码",
+      "font_class": "mima",
+      "unicode": "e652",
+      "unicode_decimal": 58962
+    },
+    {
+      "icon_id": "8288872",
+      "name": "手机",
+      "font_class": "shouji",
+      "unicode": "e853",
+      "unicode_decimal": 59475
+    },
+    {
+      "icon_id": "11518456",
+      "name": "验证码",
+      "font_class": "yanzhengma",
+      "unicode": "e634",
+      "unicode_decimal": 58932
+    },
+    {
+      "icon_id": "24968248",
+      "name": "我的",
+      "font_class": "wode",
+      "unicode": "e6ae",
+      "unicode_decimal": 59054
+    },
+    {
+      "icon_id": "6217619",
+      "name": "订 单",
+      "font_class": "order",
+      "unicode": "e682",
+      "unicode_decimal": 59010
+    },
+    {
+      "icon_id": "10195279",
+      "name": "工作台",
+      "font_class": "gongzuotai1",
+      "unicode": "e601",
+      "unicode_decimal": 58881
+    }
+  ]
+}

BIN
src/assets/iconfont.ttf


BIN
src/assets/iconfont.woff


BIN
src/assets/iconfont.woff2


+ 6 - 0
src/custom-tab-bar/index.config.ts

@@ -0,0 +1,6 @@
+export default {
+    "component": true,
+    "usingComponents": {
+
+    }
+}

+ 93 - 0
src/custom-tab-bar/index.vue

@@ -0,0 +1,93 @@
+<template>
+  <!--  <cover-view class="tab-bar">-->
+  <!--    <cover-view class="tab-bar-border"></cover-view>-->
+  <!--    <cover-view v-for="(item, index) in list" :key="index" class="tab-bar-item" @tap="tabSwitch(index, item.pagePath)">-->
+  <!--      <cover-image :src="selected === index ? item.selectedIconPath : item.iconPath" />-->
+  <!--      <cover-view :style="{ color: selected === index ? selectedColor : color }">{{item.text}}</cover-view>-->
+  <!--    </cover-view>-->
+  <!--  </cover-view>-->
+  <nut-tabbar v-model="selected" bottom safe-area-inset-bottom placeholder @tab-switch="tabSwitch"
+              unactive-color="#7d7e80" active-color="#fa2c19">
+    <nut-tabbar-item v-for="(item, index) in list" :key="index" :tab-title="item.text">
+      <template #icon="props">
+        <IconFont font-class-name="iconfont" class-prefix="icon" :name="item.icon"/>
+      </template>
+    </nut-tabbar-item>
+  </nut-tabbar>
+
+
+</template>
+
+<script setup>
+import "@nutui/nutui-taro/dist/style.css"
+import Taro from '@tarojs/taro'
+import {computed, ref, h} from 'vue'
+import {useTabBarStoreHook} from "../store/tabBar";
+import {useUuidStoreHook} from "../store/uuidStore";
+import {Order, IconFont, Dongdong} from "@nutui/icons-vue-taro";
+
+const icon = h(Dongdong)
+import {useDidShow, useLoad} from '@tarojs/taro'
+import {useUserStoreHook} from "../store/user";
+import {redirectTo} from "../utils/commonJs";
+
+const store = useTabBarStoreHook()
+const uuidStore = useUuidStoreHook()
+console.log('store', store)
+const selected = computed(() => store.getSelected())
+const color = '#000000'
+const selectedColor = '#DC143C'
+const list = [
+  {
+    pagePath: '/pages/order/index',
+    selectedIconPath: '../images/tabbar_order_on.png',
+    iconPath: '../images/tabbar_order.png',
+    text: '订单',
+    icon: 'order'
+  },
+  {
+    pagePath: '/pages/workbench/index',
+    selectedIconPath: '../images/tabbar_cate_on.png',
+    iconPath: '../images/tabbar_cate.png',
+    text: '工作台',
+    icon: 'gongzuotai1'
+  },
+  {
+    pagePath: '/pages/my/index',
+    selectedIconPath: '../images/tabbar_my_on.png',
+    iconPath: '../images/tabbar_my.png',
+    text: '我的',
+    icon: 'wode'
+  }
+]
+
+useDidShow(() => {
+  const currentPages = Taro.getCurrentPages();
+
+
+  console.log('currentPages', currentPages[currentPages.length - 1])
+  console.log('tabbar did show')
+})
+//全局登陆状态判断,如果没有登陆则跳转到登陆页面
+useLoad(() => {
+  const currentPages = Taro.getCurrentPages();
+  const {uid} = useUserStoreHook().user
+  // console.log('useUserStoreHook',useUserStoreHook().user)
+  // console.log('currentpageAAA',uid)
+  if (!uid && currentPages[currentPages.length - 1].route !== 'pages/login/index') {
+     redirectTo("/pages/login/index")
+  }
+  // console.log('useLoad')
+})
+
+function tabSwitch(item, index) {
+  Taro.switchTab({url: list[index].pagePath})
+  store.setSelected(index)
+}
+
+
+</script>
+
+<style lang="scss">
+@import '../assets/iconfont.css';
+</style>

+ 112 - 0
src/hooks/after-sale/useAfterSaleHook.ts

@@ -0,0 +1,112 @@
+import {ref, toRaw} from "vue";
+import {RefundDetailDTO, RefundQueryParamVO} from "@/types";
+import {getAfterSaleList, sellerAuditRefund} from "@/api/afterSale";
+import {usePullDownRefresh, useReachBottom} from "@tarojs/taro";
+import Taro from "@tarojs/taro";
+
+export default function useAfterSaleHook(){
+
+    /** 存储售后订单列表数据 */
+    const afterSaleList = ref<RefundDetailDTO[]>([])
+
+    /** 分页查询参数配置 */
+    const queryParam = ref<RefundQueryParamVO>({
+        page_no: 1,
+        page_size: 10,
+        refund_status: 'APPLY'
+    })
+
+    /** 标记是否已加载所有数据 */
+    const loadFinish = ref(false)
+
+    /** 获取售后订单列表数据 */
+    const loadAfterSaleList = () => {
+        const param = {...toRaw(queryParam.value)}
+        if(param.refund_status === 'ALL'){
+            delete param.refund_status
+        }
+        getAfterSaleList(param).then(res => {
+            if(queryParam.value.page_no === 1) {
+                afterSaleList.value = res.data.data
+            }else {
+                afterSaleList.value = [...afterSaleList.value, ...res.data.data]
+            }
+            if(res.data.data.length<queryParam.value.page_size){
+                loadFinish.value = true
+            }
+            afterSaleList.value = res.data.data
+            Taro.hideNavigationBarLoading();
+            Taro.stopPullDownRefresh();
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    /** 
+     * 处理售后订单审核
+     * @param refund 售后订单详情
+     * @param state 审核状态(同意/拒绝)
+     */
+    const auditAfterSale = (refund: RefundDetailDTO, state: number) => {
+        sellerAuditRefund(refund.refund.sn, {sn:refund.refund.sn, agree: state, refund_price: refund.refund?.refund_price as number}).then(res => {
+            console.log(res)
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    /** 
+     * 处理售后状态标签切换
+     * @param status 目标状态
+     */
+    const statusChange = (status: string) => {
+        queryParam.value.refund_status = status
+        queryParam.value.page_no = 1
+        loadFinish.value = false
+        loadAfterSaleList()
+    }
+
+    /** 监听滚动触底事件,加载更多数据 */
+    useReachBottom(() => {
+        if(!loadFinish.value){
+            queryParam.value.page_no++
+            loadAfterSaleList()
+        }
+    })
+
+    /** 监听下拉刷新事件,重新加载数据 */
+    usePullDownRefresh(() => {
+        queryParam.value.page_no = 1
+        loadFinish.value = false
+        Taro.showNavigationBarLoading();
+        loadAfterSaleList()
+    })
+    //售后状态标签
+    const value = ref('1')
+    //售后状态标签列表
+    const list = ref([
+      {
+        title: '申请中',
+        paneKey: 'APPLY'
+      },
+      {
+        title: '所有订单',
+        paneKey: 'ALL'
+      },
+      {
+        title: '已完成',
+        paneKey: 'COMPLETED'
+      }
+    ])
+
+    loadAfterSaleList()
+
+    return {
+        value,
+        list,
+        afterSaleList,
+        queryParam,
+        auditAfterSale,
+        statusChange
+    }
+}

+ 41 - 0
src/hooks/article/useArticleHook.ts

@@ -0,0 +1,41 @@
+import type {EsArticle} from "@/types";
+import {ref} from "vue";
+import Taro, {useLoad} from "@tarojs/taro";
+import {getArticleById} from "@/api/article";
+
+export default function useArticleHook() {
+
+    //文章详情
+    const article = ref<EsArticle>({
+        article_name: '',
+        category_id: 0,
+        content: '',
+    });
+
+    //加载文章详情
+    const loadArticle = async (id: number) => {
+        const res = await getArticleById(id);
+        article.value = res.data;
+        article.value.content = article.value.content.replace(/&nbsp;/g, "")
+        Taro.setNavigationBarTitle({
+            title: article.value.article_name
+        })
+
+    }
+
+
+    //页面显示生命周期
+    useLoad(async (option) => {
+        if (option) {
+            console.log(option)
+        }
+        if (option?.id) {
+            loadArticle(option.id as number)
+            // loadGoodsSku(option?.goods_id)
+        }
+    })
+
+    return {
+        article,
+    };
+}

+ 138 - 0
src/hooks/bill-account-edit/useBillAccountEditHook.ts

@@ -0,0 +1,138 @@
+import {computed, ref} from "vue";
+import {RebateAccountDO} from "@/types";
+import {addBillAccount, getBillAccountById, updateBillAccount} from "@/api/billAccount";
+import {useShopStoreHook} from "@/store/shopStore";
+import Taro, {useLoad} from "@tarojs/taro";
+
+export default function useBillAccountEditHook() {
+
+    //表单数据
+    const formData = ref<RebateAccountDO>({
+        id: undefined,
+        member_id: undefined,
+        bank_name: '',
+        bank_account: '',
+        account_name: '',
+        payment_plugin_id: '',
+    })
+    //表单Ref
+    const formRef = ref()
+
+    //自定义表单校验
+    const customBlurValidate = (key: string) => {
+        console.log('key', key)
+        formRef.value?.validate(key)
+    }
+
+    //银行类型
+    const bankType = ref<string[]>([]);
+
+    //银行类型选项
+    const bankTypeOptions = ref([
+        {
+            name: '支付宝',
+            value: 'alipayDirectPlugin',
+            disabled: false
+        }, {
+            name: '微信',
+            value: 'weixinPayPlugin',
+            disabled: false
+        }, {
+            name: '银行卡',
+            value: 'bankTransferPlugin',
+            disabled: false
+        }
+    ]);
+
+    //银行类型选择器是否显示
+    const bankTypePickerShow = ref(false);
+
+    //银行类型确认
+    const bankTypeConfirm = ({selectedOptions}) => {
+        console.log('selectedOptions', selectedOptions, bankType.value)
+        formData.value.payment_plugin_id = selectedOptions[0].value;
+        if (selectedOptions[0].value !== 'bankTransferPlugin'){
+            formData.value.bank_name = selectedOptions[0].name;
+       }else{
+            formData.value.bank_name=''
+        }
+        bankTypePickerShow.value = false;
+
+        customBlurValidate('payment_plugin_id')
+    }
+
+    //保存账户
+    const saveAccount =async () => {
+        const {valid:val1, errors} = await formRef.value?.validate()
+       if(val1) {
+           if (!formData.value.shop_id) {
+               formData.value.shop_id = useShopStoreHook().currentShop.shop_id as number;
+           }
+           if (!formData.value.member_id) {
+               formData.value.member_id = useShopStoreHook().currentShop.member_id as number;
+           }
+           if (!formData.value.id) {
+               addBillAccount(formData.value).then((res)=>{
+                Object.assign(formData.value, res.data)
+                   Taro.showToast({
+                       title: '添加成功',
+                       icon: 'success',
+                       duration: 2000
+                   })
+               }).catch(() => {
+
+               })
+           } else {
+               updateBillAccount(formData.value.id, formData.value).then((res)=>{
+                Object.assign(formData.value, res.data)
+                   Taro.showToast({
+                       title: '修改成功',
+                       icon: 'success',
+                       duration: 2000
+                   })
+
+               }).catch(() => {
+               })
+           }
+       }else {
+           console.log(errors)
+       }
+    }
+
+    //银行类型文本
+    const bankTypeText = computed(() => {
+        return bankType.value ? bankTypeOptions.value.find(item => item.value === bankType.value[0])?.name : ''})
+
+    //通过id获取提现账户
+    const loadAccountById = (id: number) => {
+         getBillAccountById(id).then(res => {
+            formData.value = res.data;
+            bankType.value = [res.data.payment_plugin_id as string]
+         }).catch(err => {
+            console.log(err)
+         })
+    }
+
+    //onload
+    useLoad(async (option) => {
+        if (option?.id) {
+            Taro.setNavigationBarTitle({
+                title: '编辑提现账户'
+            })
+        loadAccountById(option.id)
+        }
+
+
+    })
+    return {
+        formData,
+        formRef,
+        bankType,
+        bankTypeOptions,
+        bankTypePickerShow,
+        bankTypeText,
+        bankTypeConfirm,
+        saveAccount,
+        customBlurValidate
+    }
+}

+ 102 - 0
src/hooks/bill-account/useBillAccountHook.ts

@@ -0,0 +1,102 @@
+import {ref} from "vue";
+import type {CommonPageParam, RebateAccountDO} from "@/types";
+import {deleteBillAccount, getBillAccountList} from "@/api/billAccount";
+import {useDidShow, usePullDownRefresh, useReachBottom} from "@tarojs/taro";
+import {useShopStoreHook} from "@/store/shopStore";
+import {useUserStoreHook} from "@/store/user";
+import Taro from "@tarojs/taro";
+
+
+export default function useBillAccountHook() {
+ 
+
+    //提现账户数组
+    const rebateAccountList = ref<RebateAccountDO[]>([]);
+
+    //分页查询参数
+    const commonPageParam = ref<CommonPageParam>({
+        page_no: 1,
+        page_size: 10,
+        fixedCondition: `shop_id=${useShopStoreHook().currentShop.shop_id} and  member_id=${useUserStoreHook().user.uid}`
+    })
+
+    //是否加载完成
+    const loadFinished = ref(false);
+
+    //分页查询提现账户列表
+    const loadAccountList = () => {
+        getBillAccountList(commonPageParam.value).then(res => {
+            if (res.data.data.length < commonPageParam.value.page_size) {
+                loadFinished.value = true;
+            }
+            if (commonPageParam.value.page_no === 1) {
+                rebateAccountList.value = res.data.data;
+            } else {
+
+                rebateAccountList.value = [...rebateAccountList.value, ...res.data.data];
+            }
+            Taro.hideNavigationBarLoading();
+            Taro.stopPullDownRefresh();
+            }
+        ).catch(err => {
+            console.log(err)
+        })
+    }
+    //触底加载更多
+    useReachBottom(() => {
+        if (!loadFinished.value) {
+            commonPageParam.value.page_no++;
+            loadAccountList();
+        }
+    })
+    //下拉刷新
+    usePullDownRefresh(() => {
+        commonPageParam.value.page_no = 1;
+        loadFinished.value = false;
+        Taro.showNavigationBarLoading();
+        loadAccountList();
+    })
+
+    useDidShow(() => {
+        loadAccountList();
+    })
+
+
+
+    //删除提示内容
+    const deleteTipContent = ref('');
+    //删除弹窗是否显示
+    const deteleDialogVisible = ref(false);
+    //取消删除
+    const onCancelDelete = () => {
+        deteleDialogVisible.value = false;
+    }
+    //确认删除
+    const onOkDelete = () => {
+        deteleDialogVisible.value = false;
+        if (willDeleteAccount.value) {
+            deleteBillAccount(willDeleteAccount.value.id as number).then(()=>{
+                rebateAccountList.value = rebateAccountList.value.filter(item=>item.id !== willDeleteAccount.value?.id);
+            }).catch(()=>{
+
+            })
+        }
+    }
+    //将要删除的账户
+    const willDeleteAccount = ref<RebateAccountDO | null>(null);
+    //删除账户
+    const deleteAccount = (data: RebateAccountDO) => {
+        deleteTipContent.value = '确定删除该账户?';
+        deteleDialogVisible.value = true;
+        willDeleteAccount.value = data;
+    }
+
+    return {
+        rebateAccountList,
+        deleteTipContent,
+        deteleDialogVisible,
+        onCancelDelete,
+        onOkDelete,
+        deleteAccount
+    }
+}

+ 221 - 0
src/hooks/comment/useCommentHook.ts

@@ -0,0 +1,221 @@
+import {computed, ref, toRaw} from "vue";
+import {Comment, CommentQueryParam, CommentVO, Image} from "@/types";
+import {getCommentCount, getCommentList, replyComment} from "@/api/comment";
+import Taro, {useDidShow, usePullDownRefresh, useReachBottom} from "@tarojs/taro";
+import dayjs from "dayjs";
+import {useShopStoreHook} from "@/store/shopStore";
+import {anonymizeName} from "@/utils/commonJs";
+
+// 评论功能的自定义 Hook
+export default function useCommentHook() {
+    // 获取当前商店信息
+    const currentShopInfo = computed(() => useShopStoreHook().currentShop);
+
+    // 评论查询参数
+    const commentQueryParam = ref<CommentQueryParam>({
+        page_no: 1,
+        page_size: 10,
+        reply_status: 0,
+    })
+
+    // 标签列表
+    const tabList = ref([
+        {
+            title: '未回复',
+            paneKey: 0
+        },
+        {
+            title: '已回复',
+            paneKey: 1
+        },
+        {
+            title: '全部评价',
+            paneKey: -1
+        }
+    ])
+
+    // 是否加载完成
+    const loadFinished = ref(false);
+
+    // 评论列表数据
+    const commentList = ref<CommentVO[]>([])
+
+    // 评论总数
+    const commentCount = ref(0);
+
+    // 加载评论总数
+    const loadCommentCount = () => {
+        getCommentCount().then(res => {
+            commentCount.value = res.data;
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    // 将评论数据转换为组件所需格式
+    const commentData = computed<Comment[]>(() => {
+       return  commentList.value.map(item => ({
+           id: item.comment_id as number,
+           reply_status: item.reply_status as number,
+           info: {
+                avatar: item.member_face,
+                nickName: anonymizeName(item.member_name||''),
+                time: dayjs.unix(item.create_time as number).format('YYYY-MM-DD'),
+                content: item.content,
+                size: item.goods_name,
+                score: item.grade==='good'?5:item.grade==='bad'?Math.round(3):Math.round(4),
+           },
+              images: item.images?.map(image => ({
+                imgUrl: image
+              })),
+           reply: item.reply
+       }))
+    })
+
+    // 加载评论列表
+    const loadCommentList = () => {
+        const commentQueryParamValue = {...toRaw(commentQueryParam.value)};
+        if(commentQueryParamValue.reply_status === -1){
+            delete commentQueryParamValue.reply_status;
+        }
+        getCommentList(commentQueryParamValue).then(res => {
+            console.log('comments',res.data.data)
+            if (res.data.data.length < commentQueryParam.value.page_size) {
+                loadFinished.value = true;
+            }
+            if (commentQueryParam.value.page_no === 1) {
+                commentList.value = res.data.data;
+            } else {
+                commentList.value = [...commentList.value, ...res.data.data];
+            }
+            Taro.hideNavigationBarLoading();
+            Taro.stopPullDownRefresh();
+        }).catch(err => {
+            Taro.hideNavigationBarLoading();
+            console.log(err)
+        })
+    }
+
+    // 触底加载更多
+    useReachBottom(() => {
+        if (!loadFinished.value) {
+            commentQueryParam.value.page_no++;
+            loadCommentList();
+        }
+    })
+
+    // 下拉刷新
+    usePullDownRefresh( () => {       
+        commentQueryParam.value.page_no = 1;
+        loadFinished.value = false;
+        Taro.showNavigationBarLoading();
+        loadCommentList();
+    })
+
+    // 组件显示时加载评论列表
+    useDidShow(() => {
+        loadCommentList();
+    })
+
+    // 初始加载评论总数
+    loadCommentCount();
+
+    // 图片预览相关状态和方法
+    const showPreview = ref(false);
+    const imgData = ref<{src:string}[]|undefined>([]);
+    const initIndex = ref(0);
+
+    // 关闭预览
+    const hidePreview = () => {
+        showPreview.value = false;
+    }
+
+    // 评论图片点击
+    const commentImageClick = (index:number,images:Image[]|undefined) => {
+       console.log('click',index,images)
+        imgData.value = images?.map(item => ({
+            src: item.imgUrl as string
+        }));
+        initIndex.value = index;
+        showPreview.value = true;
+    }
+
+    // 标签切换
+    const tabChange = (panKey:number) => {
+        commentQueryParam.value.reply_status = panKey;
+        commentQueryParam.value.page_no = 1;
+        loadFinished.value = false;
+        loadCommentList();
+    }
+
+    // 回复评论相关状态和方法
+    const replyShow = ref(false);
+    const willReplyComment = ref<Comment>()
+
+    // 评论操作
+    const commentOperate = (type:string,comment:Comment) => {
+        console.log('comment',type, comment)
+        // if (comment.reply_status === 1) {
+        //     Taro.showToast({
+        //         title: '已回复',
+        //         icon: 'none'
+        //     })
+        //     return;
+        // }
+        willReplyComment.value = comment;
+        replyShow.value = true;
+    }
+
+    // 回复内容
+    const replyContent = ref('');
+
+    // 取消回复
+    const cancelReply = () => {
+        replyContent.value = '';
+        replyShow.value = false;
+    }
+
+    // 确认回复
+    const confirmReply = () => {
+        if (!replyContent.value) {
+            Taro.showToast({
+                title: '请输入回复内容',
+                icon: 'none'
+            })
+            return;
+        }
+       replyComment(willReplyComment.value?.id as number, {reply: replyContent.value}).then(() => {
+            Taro.showToast({
+                title: '回复成功',
+                icon: 'success'
+            })
+            replyContent.value = '';
+            replyShow.value = false;
+            willReplyComment.value = undefined;
+            loadCommentList();
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    // 返回所有需要在组件中使用的状态和方法
+    return {
+        commentList,
+        tabList,
+        commentQueryParam,
+        commentData,
+        currentShopInfo,
+        commentCount,
+        showPreview,
+        imgData,
+        initIndex,
+        replyShow,
+        replyContent,
+        hidePreview,
+        commentImageClick,
+        tabChange,
+        commentOperate,
+        cancelReply,
+        confirmReply,
+    }
+}

+ 232 - 0
src/hooks/cooperation-ship-detail/useCooperationShipDetailHook.tsx

@@ -0,0 +1,232 @@
+import {computed, ref} from "vue";
+import {CooperationVO} from "@/types";
+import {useShopStoreHook} from "@/store/shopStore";
+import {addCooperation, deleteCooperationShip, getCooperationDetail, handleCooperationStatus} from "@/api/cooperationShip";
+import Taro, {useLoad} from "@tarojs/taro";
+import { Tag } from '@nutui/nutui-taro'
+export default function useCooperationShipDetailHook() {
+
+    const currentShop = computed(() => useShopStoreHook().currentShop);
+
+    const formData = ref<CooperationVO>({
+        id: undefined,
+        owner_id: currentShop.value.shop_id,
+        owner_name: currentShop.value.shop_name,
+        contractor_id: 0,
+        contractor_name: '',
+        create_time: 0,
+        owner_status: 0,
+        contractor_status: 0,
+        status: '00',
+        owner_modify_time: 0,
+        contractor_modify_time: 0,
+        contractor_file: 0,
+        remark: '',
+        statement_period: 0,
+        owner_operator: currentShop.value.link_phone,
+        contractor_operator: '',
+        owner_shop_logo: currentShop.value.shop_logo,
+        contractor_shop_logo: '',
+        owner_mobile: currentShop.value.link_phone,
+        contractor_mobile: '',
+    })
+    const handleCooperationShip = () => {
+
+    }
+
+    const optionsOwner =  [
+        {
+            label: "提出合作",
+            value: 0,
+            color: "green"
+        },
+        {
+            label: "同意合作",
+            value: 1,
+            color: "red"
+        },
+        {
+            label: "终止合作",
+            value: 2,
+            color: "yellow"
+        }
+    ]
+
+    const optionsContractor =  [
+        {
+            label: "待处理",
+            value: 0,
+            color: "green"
+        },
+        {
+            label: "同意合作",
+            value: 1,
+            color: "red"
+        },
+        {
+            label: "终止合作",
+            value: 2,
+            color: "yellow"
+        }]
+
+    const formatStatus=(status:number,isOwner:boolean)=>{
+     return isOwner?optionsOwner.find(item=>item.value===status)?.label:optionsContractor.find(item=>item.value===status)?.label
+    }
+
+    const saveCooperationShip=()=> {
+     addCooperation(formData.value).then(res=>{
+         formData.value=res.data
+     }).catch(err=>{
+         console.log(err)
+     })
+    }
+
+    useLoad(async (option) => {
+        if (option?.id) {
+            Taro.setNavigationBarTitle({
+                title: '处理合作关系'
+            })
+            loadCooperationShip(option.id)
+        }
+    })
+
+    const loadCooperationShip=(id:number)=>{
+        getCooperationDetail(id).then(res=>{
+            console.log('getdetail',res.data)
+            formData.value=res.data
+        })
+    }
+
+    //判断是否显示同意按钮
+    const showAgree=():boolean =>{
+        console.log('showAgree',formData.value,currentShop.value)
+        if(Number(formData.value.owner_id)===currentShop.value.shop_id&&Number(formData.value.owner_status)===0&&formData.value.contractor_name!==""){
+            return true
+        }
+        return Number(formData.value.contractor_id) === currentShop.value.shop_id && Number(formData.value.contractor_status) === 0;
+
+    }
+    //判断是否显示驳回同意按钮
+    const showAgainstAgree=():boolean=>{
+        if(Number(formData.value.owner_id)===currentShop.value.shop_id&&Number(formData.value.owner_status)===1){
+            return true
+        }
+        return Number(formData.value.contractor_id) === currentShop.value.shop_id && Number(formData.value.contractor_status) === 1;
+
+    }
+
+    //判断是否显示恢复合作按钮
+    const showResumeAgree=():boolean =>{
+        if(Number(formData.value.owner_id)===currentShop.value.shop_id&&Number(formData.value.owner_status)===2){
+            return true
+        }
+        return Number(formData.value.contractor_id) === currentShop.value.shop_id && Number(formData.value.contractor_status) === 2;
+
+    }
+
+    //处理合作关系状态
+    const handleCooperation=(id:number,status:number)=>{
+        handleCooperationStatus(id,status).then(res=>{
+            console.log('handleCooperation',res)
+            formData.value=res.data
+        }).catch(err=>{
+            console.log(err)
+        })
+    }
+
+    //合作状态
+    const cooperationStatus = [
+        {
+            label: "待处理",
+            value: "00",
+            color: "green"
+        },
+        {
+            label: "甲方同意待乙方处理",
+            value: "10",
+            color: "red"
+        },
+        {
+            label: "乙方同意待甲方处理",
+            value: "01",
+            color: "orange"
+        },
+        {
+            label: "合作成功",
+            value: "11",
+            color: "blue"
+        },
+        {
+            label: "甲方同意乙方终止",
+            value: "12",
+            color: "purple"
+        },
+        {
+            label: "乙方终止",
+            value: "02",
+            color: "purple"
+        },
+        {
+            label: "甲方终止",
+            value: "20",
+            color: "purple"
+        },
+        {
+            label: "甲方终止乙方同意",
+            value: "21",
+            color: "orange"
+        },
+        {
+            label: "合作终止",
+            value: "22",
+            color: "purple"
+        }
+    ]
+    //渲染合作状态
+    const renderStatus=(row:CooperationVO)=> {
+        const status = cooperationStatus.find(item => item.value === row.status)
+        return (<Tag  color={status?.color} plain>{status?.label}</Tag>)
+
+    }
+
+    //删除合作关系
+    const deleteCooperation=(id:number)=>{
+        Taro.showModal({
+            title: '提示',
+            content: '确定要删除合作关系吗?',
+            success: function (res) {
+                if (res.confirm) {
+                    deleteCooperationShip(id).then(res=>{
+                        console.log('deleteCooperation',res)
+                        Taro.showToast({
+                            title: '删除成功',
+                            icon: 'success',
+                            duration: 2000,
+                            complete:()=>{
+                              setTimeout(()=>{
+                                Taro.navigateBack()
+                              },2000)
+                            }
+
+                        })
+                    }).catch(err=>{
+                        console.log(err)
+                    })
+                }
+            }
+        })
+    }
+
+    return {
+        formData,
+        handleCooperationShip,
+        formatStatus,
+        saveCooperationShip,
+        showAgree,
+        showResumeAgree,
+        showAgainstAgree,
+        handleCooperation,
+        renderStatus,
+        deleteCooperation
+    }
+}

+ 128 - 0
src/hooks/cooperation-ship/useCooperationShipHook.tsx

@@ -0,0 +1,128 @@
+import {getCooperationList} from "@/api/cooperationShip";
+import {computed, ref} from "vue";
+import type {CooperationShipQueryParam, CooperationVO} from "@/types";
+import {useShopStoreHook} from "@/store/shopStore";
+import { Tag } from '@nutui/nutui-taro'
+import {useDidShow, usePullDownRefresh, useReachBottom} from "@tarojs/taro";
+import Taro from "@tarojs/taro";
+
+// 管理合作关系功能的Hook
+export default function useCooperationShipHook() {
+
+    // 存储合作关系列表的状态
+    const cooperations = ref<CooperationVO[]>([])
+
+    // 计算属性,从store获取当前店铺
+    const currentShop = computed(() => useShopStoreHook().currentShop);
+
+    // 获取合作关系的查询参数
+    const queryParam = ref<CooperationShipQueryParam>({
+        page_no: 1,
+        page_size: 10,
+    })
+
+    // 标志是否已加载所有数据
+    const loadFinish = ref(false)
+
+    // 加载合作关系列表的函数
+    const loadCooperationList = () => {
+        getCooperationList(queryParam.value).then(res => {
+            if (queryParam.value.page_no === 1) {
+                // 如果是第一页,替换整个列表
+                cooperations.value = res.data.data;
+            } else {
+                // 对于后续页面,将新数据追加到现有列表
+                cooperations.value = [...cooperations.value, ...res.data.data];
+            }
+            // 隐藏加载指示器
+            Taro.hideNavigationBarLoading();
+            Taro.stopPullDownRefresh();
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    // 合作状态的数组
+    const cooperationStatus = [
+        {
+            label: "待处理",
+            value: "00",
+            color: "green"
+        },
+        {
+            label: "甲方同意待乙方处理",
+            value: "10",
+            color: "red"
+        },
+        {
+            label: "乙方同意待甲方处理",
+            value: "01",
+            color: "orange"
+        },
+        {
+            label: "合作成功",
+            value: "11",
+            color: "blue"
+        },
+        {
+            label: "甲方同意乙方终止",
+            value: "12",
+            color: "purple"
+        },
+        {
+            label: "乙方终止",
+            value: "02",
+            color: "purple"
+        },
+        {
+            label: "甲方终止",
+            value: "20",
+            color: "purple"
+        },
+        {
+            label: "甲方终止乙方同意",
+            value: "21",
+            color: "orange"
+        },
+        {
+            label: "合作终止",
+            value: "22",
+            color: "purple"
+        }
+    ]
+
+    // 渲染合作状态标签的函数
+    const renderStatus = (row: CooperationVO) => {
+        const status = cooperationStatus.find(item => item.value === row.status)
+        return (<Tag color={status?.color} plain>{status?.label}</Tag>)
+    }
+
+    // 下拉刷新功能的Hook
+    usePullDownRefresh(() => {
+        queryParam.value.page_no = 1
+        loadFinish.value = false
+        Taro.showNavigationBarLoading()
+        loadCooperationList()
+    })
+
+    // 无限滚动功能的Hook(到达底部时加载更多)
+    useReachBottom(() => {
+        if (loadFinish.value) {
+            return
+        }
+        queryParam.value.page_no++
+        loadCooperationList()
+    })
+
+    // 组件显示时加载合作关系列表的Hook
+    useDidShow(() => {
+        loadCooperationList()
+    })
+
+    // 返回组件中要使用的值和函数
+    return {
+        cooperations,
+        currentShop,
+        renderStatus
+    }
+}

+ 55 - 0
src/hooks/cooperation-ship/useCooperationShipSelectHook.ts

@@ -0,0 +1,55 @@
+import {computed, ref} from "vue";
+import {useShopStoreHook} from "@/store/shopStore";
+import {type CooperationShipQueryParam, CooperationVO} from "@/types";
+import {getCooperationList} from "@/api/cooperationShip";
+
+
+
+export default function useCooperationShipSelectHook() {
+    const currentShop = computed(() => useShopStoreHook().currentShop);
+    //合作单位列表
+    const cooperationOptions = ref<CooperationVO[]>([])
+
+    //合作查询参数
+    const cooperationQueryParam = ref<CooperationShipQueryParam>({
+        page_no: 1,
+        page_size: 100,
+    })
+    // 合作单位下拉框
+    const cooperations = computed(() => {
+        var opts = cooperationOptions.value.map(item => {
+            return {
+                text: item.owner_id === currentShop.value.shop_id ? item.contractor_name : item.owner_name,
+                value: item.owner_id === currentShop.value.shop_id ? item.contractor_id : item.owner_id,
+            } as {text?: string, value?: number}
+        })
+        opts = [{text: '所有单位', value: 0}, ...opts]
+        return opts;
+    })
+    // 合作单位Picker选择项目
+    const coopForPicker = computed(() => {
+        return cooperationOptions.value.map(item => {
+            return {
+                text: item.owner_id === currentShop.value.shop_id ? item.contractor_name : item.owner_name,
+                value: item.owner_id === currentShop.value.shop_id ? item.contractor_id : item.owner_id,
+            } as {text?: string, value?: number}
+        })
+    })
+    // 加载合作单位
+    const loadCooperationShips = () => {
+        getCooperationList(cooperationQueryParam.value).then(res => {
+            cooperationOptions.value = res.data.data
+            console.log(res)
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+
+    // 加载合作单位
+    loadCooperationShips()
+  return {
+    cooperations,
+    coopForPicker
+  };
+}

+ 125 - 0
src/hooks/mqtt-device/useMqttDeviceEditHook.ts

@@ -0,0 +1,125 @@
+import { nextTick, ref } from 'vue'
+import type { MqttDevice } from '@/types'
+import { useLoad } from '@tarojs/taro'
+import Taro from '@tarojs/taro'
+import { addMqttDevice, updateMqttDevice } from '@/api/mqttDevice'
+import { useShopStoreHook } from '@/store/shopStore'
+
+export default function useMqttDeviceEditHook() {
+    // 创建一个响应式的设备表单对象
+    const deviceForm = ref<MqttDevice>({
+        device_type: 1,
+        device_key: '',
+        shop_id: useShopStoreHook().currentShop.shop_id as number
+    })
+    //表单引用
+    const formRef = ref()
+    // 定义可选的设备类型列表
+    const deviceTypes = [
+        { text: '云打印', value: 1 },
+        { text: '云音响', value: 2 },
+    ]
+
+    // 控制设备类型选择器的显示状态
+    const showDeviceTypePicker = ref(false)
+
+    // 处理设备类型选择确认事件
+    const onConfirm = ({ selectedValue, selectedOptions }) => {
+        deviceForm.value.device_type = selectedValue[0]
+        showDeviceTypePicker.value = false
+        nextTick(() => {
+            customBlurValidate('deviceType')
+        })
+    }
+
+    // 处理表单提交
+    const submitForm = () => {
+        // 这里可以添加表单验证逻辑
+        formRef.value.validate().then(({ valid, errors }) => {
+            if (valid) {
+                if (deviceForm.value.id) {
+                    updateMqttDevice(deviceForm.value,deviceForm.value.id).then(res => {
+                        
+                        Taro.showToast({
+                            title: '修改成功',
+                        icon: 'success',
+                            duration:2000
+                        })
+                    }).catch(err => {
+                        Taro.showToast({
+                            title: err.data.message,
+                            icon: 'none'
+                        })
+                    })
+                } else {
+                    addMqttDevice(deviceForm.value).then(res => {
+                        Object.assign(deviceForm.value, res.data)
+                        if(res.data.success){
+                            Taro.showToast({
+                                title: '添加成功',
+                            icon: 'success',
+                                duration:2000
+                            })
+                        }else{
+                            Taro.showToast({
+                                title: res.data.message || '添加失败',
+                                icon: 'error'
+                            })
+                        }
+                    }).catch(err => {
+                        Taro.showToast({
+                            title: err.data.message,
+                            icon: 'error'
+                        })
+                    })
+                }
+            } else {
+                Taro.showToast({
+                    title: '请检查表单',
+                    icon: 'none'
+                })
+            }}).catch(err => {
+                console.log(err)
+            })
+    }
+
+    //自定义表单验证
+    const customBlurValidate = (key: string) => {
+        formRef.value.validate(key)
+    }
+
+    //onload
+    useLoad(async (option) => {
+        if (option?.id) {
+            Taro.setNavigationBarTitle({
+                title: '编辑设备'
+            })
+
+            // 设置 id, 
+            deviceForm.value.id = option.id
+           
+        } else {
+            Taro.setNavigationBarTitle({
+                title: '添加设备'
+            })
+        }
+        //设置设备key和设备类型
+        if(option?.device_key){
+            deviceForm.value.device_key = option.device_key
+        }
+        if(option?.device_type){
+            deviceForm.value.device_type = Number(option.device_type)
+        }
+    })
+
+    // 返回hook中的响应式数据和方法
+    return {
+        deviceForm,
+        formRef,
+        deviceTypes,
+        showDeviceTypePicker,
+        onConfirm,
+        submitForm,
+        customBlurValidate,
+    }
+}

+ 161 - 0
src/hooks/mqtt-device/useMqttDeviceHook.ts

@@ -0,0 +1,161 @@
+import { deleteMqttDevice, getMqttDeviceList } from "@/api/mqttDevice"
+import { useShopStoreHook } from "@/store/shopStore"
+import type { CommonPageParam, MqttDevice } from "@/types"
+import { navigateTo } from "@/utils/commonJs"
+import { SwipeInstance } from "@nutui/nutui-taro"
+import Taro, { useDidShow } from "@tarojs/taro"
+import { usePullDownRefresh, useReachBottom } from "@tarojs/taro"
+import { nextTick, ref, watch } from "vue"
+
+export default function useMqttDeviceHook() {
+    // 存储设备列表的响应式引用
+    const deviceList = ref<MqttDevice[]>([])
+
+    // 查询参数,用于分页和筛选
+    const queryParams = ref<CommonPageParam>({
+        page_no: 1,
+        page_size: 10,
+        fixedCondition: `shop_id=${useShopStoreHook().currentShop.shop_id}`
+    })
+
+    // 存储 Swipe 组件实例的数组
+    const swipeRefs = ref<(SwipeInstance|null)[]>([])
+
+    // 标记是否已加载所有数据
+    const isLoaded = ref(false)
+
+    // 获取设备列表的函数
+    const loadDeviceList = () => {
+        getMqttDeviceList(queryParams.value).then((res) => {
+            // 如果返回的数据少于页面大小,说明已加载所有数据
+            if (res.data.data.length < queryParams.value.page_size) {
+                isLoaded.value = true
+            }
+            deviceList.value = res.data.data
+            Taro.hideNavigationBarLoading()
+            Taro.stopPullDownRefresh()
+            
+            // 在下一个 tick 关闭所有打开的 Swipe 组件
+            nextTick(()=>{
+               swipeRefs.value.forEach((swipe:SwipeInstance|null)=>{
+                swipe?.close()
+               })
+            })
+        })
+    }
+
+    // 处理下拉刷新
+    usePullDownRefresh(() => {
+        Taro.showNavigationBarLoading()
+        isLoaded.value = false
+        queryParams.value.page_no = 1
+        loadDeviceList()
+    })
+
+    // 处理上拉加载更多
+    useReachBottom(() => {
+        if (isLoaded.value) {
+            return
+        }
+        queryParams.value.page_no++
+        loadDeviceList()
+    })
+
+    // 扫码添加设备
+    const scanAddDevice = () => {
+        Taro.scanCode({
+            success: (res) => {
+                const result = res.result
+                try {
+                    const url = new URL(result);
+                    const params = new URLSearchParams(url.search);
+                    const type = params.get('type');
+                    const mac = params.get('mac');
+
+                    // 验证扫描结果的有效性
+                    if (url.hostname !== 'm.wdklian.com' || !type || !mac) {
+                        Taro.showToast({
+                            title: '无效的二维码',
+                            icon: 'error',
+                            duration: 2000
+                        });
+                        return;
+                    }
+                  if(type === 'BROADCAST'||type === 'PRINTER'){
+                    // 导航到设备编辑页面
+                    navigateTo(
+                        `/subPackageA/pages/shop-device-edit/index`,
+                        {
+                            device_key:mac,
+                            device_type:type === 'BROADCAST' ? 2 : 1
+                        }
+                    );
+                  }else{
+                    Taro.showToast({
+                        title: '无效的二维码',
+                        icon: 'error',
+                        duration: 2000
+                    });
+                  }
+                } catch (error) {
+                    Taro.showToast({
+                        title: '解析二维码失败',
+                        icon: 'error',
+                        duration: 2000
+                    });
+                }
+            }
+        })
+    }
+
+    // 存储要删除的设备 ID
+    const deleteId = ref<number|undefined>()
+
+    // 删除设备的函数
+    const deleteDevice = (item:MqttDevice) => {
+        deleteId.value = item.id
+        showDeleteDialog.value = true
+    }
+
+    // 确认删除设备
+    const confirmDelete = () => {
+        if(deleteId.value){
+            deleteMqttDevice(deleteId.value).then(() => {
+                Taro.showToast({
+                    title: '删除成功',
+                    icon: 'success',
+                    duration: 2000,
+                    complete:()=>{
+                        loadDeviceList()
+                    }
+                })
+            })
+        }
+    }
+
+    // 控制删除对话框的显示
+    const showDeleteDialog = ref(false)
+
+    // 监听删除对话框的显示状态,关闭时同时关闭所有 Swipe 组件
+    watch(showDeleteDialog,()=>{
+        if(!showDeleteDialog.value){
+            swipeRefs.value.forEach((swipe:SwipeInstance|null)=>{
+                swipe?.close()
+            })
+        }
+    })
+
+    // 页面显示时加载设备列表
+    useDidShow(()=>{
+        loadDeviceList()
+    })
+
+    return {
+        deviceList,
+        showDeleteDialog,
+        scanAddDevice,
+        deleteDevice,
+        confirmDelete,
+        swipeRefs
+    }
+}

+ 107 - 0
src/hooks/order/useOrderHook.ts

@@ -0,0 +1,107 @@
+import {ref} from "vue"
+import {deliveryGoods, getTradeList} from "@/api/trade";
+import Taro from "@tarojs/taro";
+import {TradeQueryParam, TradeVO} from "@/types/order";
+import { useShopStoreHook } from "@/store/shopStore";
+
+export default function useOrderHook() {
+
+    const queryParam = ref<TradeQueryParam>({
+        page_no: 1,
+        page_size: 10,
+    })
+
+    const trades = ref<TradeVO[] | undefined>()
+
+    //加载订单
+    const loadTrades = () => {
+        getTradeList(queryParam.value).then(res => {
+            trades.value = res.data.data
+            console.log(trades.value)
+        });
+    }
+    //拨打电话
+    const contractUser = (phone) => {
+
+        Taro.makePhoneCall({
+            phoneNumber: phone
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    //订单发货
+    const shipOrder = (trade) => {
+        Taro.showModal({
+            title: '提示',
+            content: '订单目前只支持线下自行配送,确认发货吗?',
+            success: function (res) {
+                if (res.confirm) {
+                    deliveryGoods(trade.sn, {
+                        'ship_no': '线下配送',
+                        'logi_id': 0,
+                        'logi_name': '线下配送'
+                    }).then(() => {
+                        loadTrades()
+                        Taro.showToast({
+                            title: '发货成功',
+                            icon: 'success',
+                            duration: 2000
+                        })
+
+                    })
+                } else if (res.cancel) {
+                    console.log('用户点击取消')
+                }
+            }
+        })
+    }
+
+    const orderStatus = ref([
+        {
+            title: '全部订单',
+            paneKey: 'ALL'
+        },
+        {
+            title: '待发货',
+            paneKey: 'WAIT_SHIP'
+        },
+        {
+            title: '已发货',
+            paneKey: 'WAIT_ROG'
+        },
+        {
+            title: '已完成',
+            paneKey: 'COMPLETE'
+        },
+        {
+            title: '已取消',
+            paneKey: 'CANCELLED'
+        }
+    ])
+    const currentStatus = ref('WAIT_SHIP')
+    if(useShopStoreHook().currentShop.shop_id){
+        loadTrades()
+    }
+    const search = () => {
+        queryParam.value.page_no = 1
+        queryParam.value.order_status = currentStatus.value
+        loadTrades()
+    }
+
+    const statusChange = (state) => {
+        currentStatus.value = state
+        search()
+    }
+
+    return {
+        trades,
+        orderStatus,
+        currentStatus,
+        queryParam,
+        contractUser,
+        shipOrder,
+        search,
+        statusChange
+    }
+}

+ 145 - 0
src/hooks/product-group/useProductGroupHook.tsx

@@ -0,0 +1,145 @@
+import { onBeforeMount, ref} from "vue";
+import {addShopCat, deleteShopCat, getShopCatList, updateShopCat} from "@/api/shopcat";
+import {ProductGroup} from "@/types";
+import { useShopCategoryStoreHook } from "@/store/shopCategoryStore";
+
+export default function useProductGroupHook(){
+    // ===== 状态定义 =====
+    // 存储所有商品分组的数据列表
+    const groups = ref<ProductGroup[]>([])
+    // 当前正在操作(编辑/删除)的分组对象
+    const currentGroup = ref<ProductGroup|null>(null)
+    // 存储所有分组项的滑动操作引用
+    const swipeRefs = ref<any[]>([])
+
+    // ===== 编辑相关状态 =====
+    // 控制分组编辑弹窗的显示和隐藏
+    const groupEditDialogVisible = ref(false)
+    // 分组名称输入值
+    const groupName = ref('')
+
+    // ===== 删除相关状态 =====
+    // 删除分组时的确认提示文本
+    const deleteTipContent = ref('确定删除该分组吗?')
+    // 控制删除确认弹窗的显示状态
+    const deteleDialogVisible = ref(false)
+
+    // ===== 生命周期钩子 =====
+    onBeforeMount(async () => {
+        try{
+            const{data:group} = await getShopCatList()
+            groups.value = group
+            useShopCategoryStoreHook().setShopCategoryList(group)
+        }catch (e) {
+            console.log(e)
+        }
+    })
+
+    // ===== 数据操作方法 =====
+    // 从服务器获取最新的分组列表数据
+    const getShopCats = () => {
+        getShopCatList().then(res => {
+            groups.value=[...res.data]
+            console.log(res)
+        })
+    }
+
+    // ===== 编辑相关方法 =====
+    // 添加分组
+    const handAdd = () => {
+        groupEditDialogVisible.value = true
+        groupName.value = ''
+    }
+
+    // 编辑分组
+    const editShopCat = (group) => {
+        currentGroup.value = group
+        groupEditDialogVisible.value = true
+        groupName.value = group.shop_cat_name
+    }
+
+    // 编辑弹窗确认
+    const onOk = () => {
+        groupEditDialogVisible.value = false
+        if(currentGroup.value?.shop_cat_id){
+            // 编辑现有分组
+            updateShopCat({
+                shop_cat_id: currentGroup.value.shop_cat_id,
+                shop_cat_name: groupName.value,
+                disable: 1,
+                sort: 0
+            }, currentGroup.value.shop_cat_id).then(()=>{
+                currentGroup.value = null
+                closeSwipe()
+                getShopCats()
+            })
+        }else{
+            // 添加新分组
+            addShopCat({
+                shop_cat_name: groupName.value,
+                disable: 1,
+                sort: 0
+            }).then(()=>{
+                getShopCats()
+            })
+        }
+    }
+
+    // 编辑弹窗取消
+    const onCancel = () => {
+        groupEditDialogVisible.value = false
+    }
+
+    // ===== 删除相关方法 =====
+    // 显示删除确认框
+    const removeShopCat = (group) => {
+        deteleDialogVisible.value = true
+        deleteTipContent.value = `确定删除分组[${group.shop_cat_name}]吗?`
+        currentGroup.value = group
+    }
+
+    // 确认删除
+    const onOkDeleteGroup = () => {
+        deteleDialogVisible.value = false
+        if(currentGroup.value) {
+            deleteShopCat(currentGroup.value.shop_cat_id).then(() => {
+                currentGroup.value = null
+                getShopCats()
+            })
+        }
+    }
+
+    // 取消删除
+    const onCancelDeleteGroup = () => {
+        deteleDialogVisible.value = false
+        currentGroup.value = null
+        closeSwipe()
+    }
+
+    // ===== 工具方法 =====
+    // 关闭所有打开的滑动操作面板
+    const closeSwipe = () => {
+        swipeRefs.value.forEach(swipe => {
+            swipe?.close()
+        })
+    }
+
+    return {
+        // 状态
+        groups,
+        groupName,
+        swipeRefs,
+        // 弹窗控制
+        groupEditDialogVisible,
+        deteleDialogVisible,
+        deleteTipContent,
+        // 方法
+        handAdd,
+        editShopCat,
+        removeShopCat,
+        onOk,
+        onCancel,
+        onOkDeleteGroup,
+        onCancelDeleteGroup,
+    }
+}

+ 83 - 0
src/hooks/product-manager/useProductManagerHook.ts

@@ -0,0 +1,83 @@
+import {reactive, ref} from "vue";
+import Taro, {usePullDownRefresh,useReachBottom} from "@tarojs/taro";
+import {getGoodsList, putOffGoods, putOnGoods} from "@/api/goods";
+import {type GoodsQueryParam, type GoodsVO} from "@/types";
+
+export function useProductManagerHook() {
+
+    //页面显示生命周期
+    const goods=ref<GoodsVO[]>([])
+
+    //商品查询参数
+    const queryParam = reactive<GoodsQueryParam>({
+        page_no: 1,
+        page_size: 10,
+    })
+
+   
+    //加载完成
+    const loadFinish = ref(false)
+
+    //加载商品分页数据
+    const loadGoods = () => {
+        getGoodsList(queryParam).then(res => {
+            if(queryParam.page_no===1){//重新加载
+                goods.value = [...res.data.data]
+            }else{ //触底加载
+                goods.value = [...goods.value,...res.data.data]
+            }
+            if(res.data.data.length<queryParam.page_size){
+                loadFinish.value = true
+            }
+            Taro.hideNavigationBarLoading()
+            Taro.stopPullDownRefresh()
+            console.log(res)
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+   //下拉刷新
+    usePullDownRefresh(() => {
+        queryParam.page_no = 1
+        Taro.showNavigationBarLoading()
+        loadGoods()
+    })
+    //触底加载更多
+    useReachBottom(() => {
+        if(loadFinish.value){
+            return
+        }
+        queryParam.page_no++
+        loadGoods()
+    })
+   
+    // 改变商品状态
+    const changeGoodsStatus = (goodsId:number,status:number) => {
+       if(status===1){
+           //下架
+       putOffGoods(goodsId).then(() => {
+           Taro.showToast({
+                title:'产品已下架',
+                icon:'success'
+              })
+           loadGoods()
+       })
+    }else {
+        //上架
+        putOnGoods(goodsId).then(() => {
+            Taro.showToast({
+                title:'上架成功',
+                icon:'success'
+            })
+            loadGoods()
+        })
+       }
+    }
+
+    return {
+        queryParam,
+        goods,
+        loadGoods,
+        changeGoodsStatus
+    };
+}

+ 94 - 0
src/hooks/product-manager/useProductSearchHook.ts

@@ -0,0 +1,94 @@
+import {ref} from "vue";
+import type {Category} from "@nutui/icons-vue-taro";
+import { useLoad } from "@tarojs/taro";
+import {getCategoryList} from "@/api/category";
+import { ProductGroup } from "@/types";
+import { getShopCatList } from "@/api/shopcat";
+import { useShopCategoryStoreHook } from "@/store/shopCategoryStore";
+import { useShopStoreHook } from "@/store/shopStore";
+import { useCategoryStoreHook } from "@/store/categoryStore";
+
+export default function useProductSearchHook(loadGoods: () => void, queryParam) {
+    // 分类选择器显示
+    const show = ref(false)
+    // 分类选择器值
+    const value = ref<Number[]>([0])
+    // 分类选择器选项
+    const options = ref<Category[]>([])
+
+    // 分类选择器改变事件
+    const change = (val) => {
+        // show.value = false
+        const catRootId=  useShopStoreHook().currentShop?.shop_type==='2'?0:-1;
+        queryParam.category_path = `${catRootId}|${val.join('|')}`
+        queryParam.page_no = 1
+        // loadGoods()
+    }
+    // 重置分类选择
+    const resetCatId = () => {
+        value.value=[0]
+        queryParam.category_path = ''
+        queryParam.page_no=1
+        shopCatValue.value=''
+        queryParam.shop_cat_path=''
+        loadGoods()
+        show.value = false
+    }
+    // 搜索
+    const search = () => {
+        // if ((queryParam.keyword ?? '') === '') {
+        //     Taro.showToast({title: '请输入搜索内容', icon: 'none'})
+        //     return
+        // }
+        queryParam.page_no = 1
+        loadGoods()
+        show.value = false
+    }
+    // 加载分类,商品页面加载分类数据,存入store,发布页面从store获取分类数据
+    const loadCategories = () => {        
+       const catRootId=  useShopStoreHook().currentShop?.shop_type==='2'?0:-1;
+        getCategoryList(catRootId).then(res => {
+            options.value = [...res.data]
+            useCategoryStoreHook().setCategoryList(res.data)
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+   // 商品分组选择器选项
+   const shopCatOption = ref<ProductGroup[]>([])
+   // 商品分组选择器值
+   const shopCatValue = ref<string|undefined>(undefined)
+
+    // 加载商品分组
+    getShopCatList().then(res=>{
+        shopCatOption.value = res.data
+        useShopCategoryStoreHook().setShopCategoryList(res.data)
+    })
+    const shopCatClick=(option:any)=>{
+        shopCatValue.value=option.cat_path,
+        queryParam.shop_cat_path=option.cat_path
+    }
+
+    useLoad(async (options) => {
+        if(options?.cat_path){
+            shopCatValue.value=options.cat_path
+            queryParam.shop_cat_path=options.cat_path
+        }
+        loadGoods()
+    })
+
+    // 调用加载分类方法
+    loadCategories();
+    return {
+        value,
+        options,
+        show,
+        shopCatOption,
+        shopCatValue,
+        change,
+        resetCatId,
+        search,
+        shopCatClick
+    }
+}

+ 72 - 0
src/hooks/product-manager/useProductSkuQuantityHook.ts

@@ -0,0 +1,72 @@
+import {getGoodsSku, updateSkuQuantity} from "@/api/goods";
+import {type SkuEditItem} from "@/types";
+import {ref} from "vue";
+import Taro from "@tarojs/taro";
+
+export default function useProductSkuQuantityHook(loadGoods: () => void) {
+
+    //商品sku列表
+    const goodsSkuList = ref<SkuEditItem[]>([])
+    //商品名称
+    const goodsName = ref('')
+    //商品id
+    const goodsId = ref(0)
+    //修改sku数量弹窗是否显示
+    const updateSkuQuantityVisible = ref(false)
+    //加载商品sku列表
+    const loadGoodsSku = (goods_id: number, goods_name: string) => {
+        goodsName.value = goods_name
+        goodsId.value = goods_id
+        getGoodsSku(goods_id).then(res => {
+            goodsSkuList.value = [...res.data]
+            updateSkuQuantityVisible.value = true
+
+        })
+    }
+    //确定修改sku数量
+    const onOkSkuQuantity = () => {
+        formRefSku.value?.validate().then(({valid, errors}) => {
+            if (valid) {
+                const quantitys = goodsSkuList.value.map(item => {
+                        return {
+                            sku_id: item.sku_id,
+                            quantity_count: item.quantity
+                        }
+                    }
+                );
+                updateSkuQuantity(quantitys, goodsId.value).then(() => {
+                    updateSkuQuantityVisible.value = false
+                    loadGoods()
+                    Taro.showToast({
+                        title: '修改成功',
+                        icon: 'success',
+                        duration: 2000
+                    })
+                })
+            } else {
+                console.log('errors', errors)
+            }
+
+
+        })
+    }
+    //sku表单
+    const formRefSku = ref()
+    // 失去焦点校验
+    const customBlurValidate = (prop) => {
+        console.log('prop', formRefSku.value)
+        formRefSku.value?.validate(prop)
+    }
+
+
+    return {
+        goodsSkuList,
+        goodsName,
+        formRefSku,
+        updateSkuQuantityVisible,
+        loadGoodsSku,
+        onOkSkuQuantity,
+        customBlurValidate
+    }
+
+}

+ 589 - 0
src/hooks/product-publish/useProductPublishHook.ts

@@ -0,0 +1,589 @@
+import {type GoodsDTO, SkuEditItem} from "@/types/goods";
+import {reactive, Ref, ref, watch, watchEffect} from "vue";
+import {type Category} from "@nutui/icons-vue-taro";
+import {type ProductGroup, Specification} from "@/types";
+import Taro, {useLoad} from "@tarojs/taro";
+import {getCategoryList} from "@/api/category";
+import {getShopCatList} from "@/api/shopcat";
+import {getSpecificationList} from "@/api/specification";
+import {addGoods, getGoodsDetail, getGoodsSku, updateGoods} from "@/api/goods";
+import {findPath, redirectTo} from "@/utils/commonJs";
+import { FileItem } from "@nutui/nutui-taro/dist/types/__VUE/uploader/type";
+import { baseBaseUrl } from "@/http/baseUrl";
+import { useShopCategoryStoreHook } from "@/store/shopCategoryStore";
+import { useShopStoreHook } from "@/store/shopStore";
+import { useCategoryStoreHook } from "@/store/categoryStore";
+import { uploadFile } from "@/api/file";
+
+
+export function  useProductPublishHook() {
+
+    // 商品表单数据主体
+    const formData = reactive<GoodsDTO>({
+        category_id: 0,
+        category_name: '',
+        shop_cat_id: 0,
+        shop_cat_name: '',
+        brand_id: undefined,
+        goods_name: '',
+        sn: '',
+        price: 0,
+        cost: 0,
+        mktprice: 0,
+        weight: 0,
+        exchange: undefined,
+        goods_gallery_list: undefined,
+        goods_id: undefined,
+        goods_params_list: undefined,
+        goods_transfee_charge: 1,
+        has_changed: 0,
+        intro: undefined,
+        market_enable: 1,
+        meta_description: undefined,
+        meta_keywords: undefined,
+        page_title: undefined,
+        quantity: undefined,
+        sku_list: [],
+        template_id: 0
+
+    })
+
+    // 分类选择相关状态
+    const visible = ref(false);
+    const categoryValue = ref<number[]>([]);
+    const options = ref<Category[]>([])
+
+    // 商品分组选择相关状态
+    const shopCatShow = ref(false)
+    const shopCatOption = ref<ProductGroup[]>([])
+    const shopCatValue = ref<number[]>([])
+
+    // 规格选择相关状态
+    const specChooseVisible = ref(false)
+    const specList = ref<Specification[]>([])
+    const specSelectValue = ref<string[]>([])
+    const goodsSkus: Ref<SkuEditItem[]> = ref([])
+
+    // 表单校验引用
+    const formRef = ref()
+    const formRefSku = ref()
+
+    // 图片上传相关状态
+    const goodsImages = ref<any[]>([])
+
+    // 分类选择器改变事件
+    const change = (val, pathNodes) => {
+        console.log('val', val)
+        formData.category_id = val[val.length - 1]
+        formData.category_name = pathNodes.map(item => item.text).join('/')
+        customBlurValidate('category_name')
+    }
+
+    // 商品分组选择器确认
+    const shopCatConfirm = ({selectedOptions}) => {
+        formData.shop_cat_id = selectedOptions[0].shop_cat_id
+        formData.shop_cat_name = selectedOptions[0].shop_cat_name
+        shopCatShow.value = false
+        customBlurValidate('shop_cat_name')
+    }
+
+    // 页面显示生命周期
+    useLoad(async (option) => {
+        if (option) {
+            console.log(option)
+        }
+        // //加载分类
+        // const {data: categorys} = await getCategoryList(-1)
+        // options.value = categorys
+        // //加载商品分组
+        // const {data: groups} = await getShopCatList()
+        // shopCatOption.value = groups
+
+
+        if (option?.goods_id) {
+            Taro.setNavigationBarTitle({
+                title: '编辑商品'
+            })
+            loadGoodsDetail(option?.goods_id)
+            loadGoodsSku(option?.goods_id)
+        }
+
+
+    })
+
+    // 保存商品
+    const publishGood = async () => {
+        formData.goods_gallery_list = goodsImages.value.map((item, index) => {
+            return {
+                original: item.url,
+                img_id: (typeof item.uid === 'string') ? -1 : item.uid,
+                sort: index
+            }
+        })
+        formData.has_changed = 1
+        const {valid:val1, errors} = await formRef.value?.validate()
+        const {valid:val2, errors:errors2} = await formRefSku.value?.validate()
+        //详情图片,转成html
+        if(introImages.value.length>0){
+            formData.intro = `<p style="font-size:0;line-height:0;">${introImages.value.map(item => `<img src="${item}" mode="widthFix" alt="" />`).join('')}</p>`
+            console.log('formData.intro',formData.intro)
+        }else{
+            formData.intro = ""
+        }
+        console.log('validate',val1,val2,errors,errors2)
+        if(val1&&val2){
+            if (formData.goods_id) {
+                //编辑商品
+                updateGoods(formData).then(() => {
+                    Taro.showToast({
+                        title: '更新成功',
+                        icon: 'success',
+                        duration: 2000
+                    })
+                }).catch(error => {
+                  Taro.showToast({
+                        title: error.msg,//'编辑失败',
+                        icon: 'none',
+                        duration: 2000
+                    })
+                })
+            }else{//添加商品
+              addGoods(formData).then((res)=>{
+                Object.assign(formData, res.data)
+                const categoryPath = findPath(options.value, res.data.category_id)
+                categoryValue.value = [...categoryPath.map(item => item.category_id)]
+                console.log('categoryPath',categoryValue)
+                formData.category_name = categoryPath.map(item => item.name).join('/')
+                shopCatValue.value = [res.data.shop_cat_id as number]
+                formData.shop_cat_name = shopCatOption.value.find(item => item.shop_cat_id === res.data.shop_cat_id)?.shop_cat_name
+                Taro.showToast({
+                    title: '添加成功',
+                    icon: 'success',
+                    duration: 2000
+                })
+              })
+            }
+        }
+    }
+
+    
+   
+    //加载规格
+    const loadSpecifications = (data) => {
+        getSpecificationList(data).then(res => {
+            specList.value = [...res.data]
+            // console.log('specs',specs.value)
+        })
+    }
+    //确定选择规格
+    const onOkspecChoose = () => {
+        console.log('specSelect', specSelectValue.value)
+        const specValues = specList.value.flatMap(item => item.value_list).filter(p => {
+            return specSelectValue.value.includes(`${p?.spec_id}_${p?.spec_value_id}`)
+        })
+        const groupSpecValue = groupBy(specValues, 'spec_id')
+        console.log('specValues', groupSpecValue)
+
+        console.log('cartesianProduct', cartesianProduct(groupSpecValue))
+        const specCombine = cartesianProduct(groupSpecValue);
+        specCombine.forEach(item => {
+            if(!formData.sku_list?.some(p=>p.key===item.map(p=>p.spec_value_id).sort().join('_'))){
+                if(goodsSkus.value.some(p=>p.key===item.map(p=>p.spec_value_id).sort().join('_'))){
+                   const oldSku = goodsSkus.value.find(p=>p.key===item.map(p=>p.spec_value_id).sort().join('_'))||null
+                    console.log('oldSku',oldSku)
+                    if(oldSku) {
+                        formData.sku_list?.push(oldSku)
+                    }
+                }else{
+                    formData.sku_list?.push({
+                        cost: 0,
+                        price: 0,
+                        quantity: 0,
+                        sn: '',
+                        weight: 0,
+                        spec_list: [...item],
+                        sku_id: undefined,
+                        key: item.map(p => p.spec_value_id).sort().join('_')
+                    })
+                }
+            }
+        })
+        formData.sku_list = formData.sku_list?.filter(item => specCombine.some(p => p.map(p => p.spec_value_id).sort().join('_') === item.key))
+
+        // formData.sku_list = cartesianProduct(groupSpecValue)
+        specChooseVisible.value = false
+    }
+    //加载商品详情
+    const loadGoodsDetail = (id: number) => {
+        getGoodsDetail(id).then(res => {
+            Object.assign(formData, res.data)
+            console.log('options',options.value)
+            const categoryPath = findPath(options.value, res.data.category_id)
+            categoryValue.value = [...categoryPath.map(item => item.category_id)]
+            console.log('categoryValue',categoryValue)
+            formData.category_name = categoryPath.map(item => item.name).join('/')
+            shopCatValue.value = [res.data.shop_cat_id as number]
+            formData.shop_cat_name = shopCatOption.value.find(item => item.shop_cat_id === res.data.shop_cat_id)?.shop_cat_name
+            //详情图片,提取图片地址
+            if(res.data.intro){
+                const matches = res.data.intro.match(/<img[^>]*src="([^"]*)"[^>]*>/g)
+                introImages.value = matches ? matches.map(item => {
+                    const match = item.match(/src="([^"]*)"/);
+                    return match ? match[1] : null;
+                }).filter(Boolean) as string[] : [];
+                console.log('introImages', introImages.value)
+            }
+          
+        }).catch(() => {
+        })
+    }
+   
+    //查询商品SKU
+    const loadGoodsSku = (goodsId: number) => {
+        getGoodsSku(goodsId).then(res => {
+            goodsSkus.value = res.data.map(item => {
+                return {
+                    ...item,
+                    key: item.spec_list.map(p => p.spec_value_id).sort().join('_')
+                }
+            })
+
+            if(formData.sku_list){
+                Object.assign(formData.sku_list, [...goodsSkus.value])
+            }
+            console.log('sku', goodsSkus.value)
+        }).catch(() => {
+         
+        })
+    }
+    const goodsImagesUploader = ref()
+    const fileChange=async ({fileList,event})=>{
+        // console.log('fileChange',fileList,event)
+        // console.log('上传前',goodsImages.value)
+        // const file:FileItem = fileList[fileList.length-1];
+        // Taro.cropImage({
+        //     src: fileList[fileList.length-1].url,
+        //     cropScale: '1:1', // 裁剪比例
+        //     success: (res) => {
+        //         file.url = res.tempFilePath
+        //         file.name = res.tempFilePath
+        //         file.path = res.tempFilePath
+        //         file.status="ready"
+        //         goodsImages.value[fileList.length-1]=file
+        //         // console.log(res.tempFilePath)
+        //         // goodsImages.value[fileList.length-1].url = res.tempFilePath
+        //         // goodsImages.value[fileList.length-1].name = res.tempFilePath
+        //         // goodsImages.value[fileList.length-1].path = 'ready'
+        //         goodsImagesUploader.value.submit()
+        //     }
+        // })
+
+        const file: FileItem = fileList[fileList.length - 1]
+  
+        try {
+          // 从商品图片列表中移除当前文件
+          goodsImages.value = goodsImages.value.filter(item => item.uid !== file.uid)
+          
+          // 添加一个带上传状态的临时文件
+          const tempFile = {
+            ...file,
+            status: 'uploading',
+            message: '处理中',
+            percentage: 0
+          }
+          goodsImages.value.push(tempFile)
+          
+          // 裁剪图片
+          const cropResult = await new Promise((resolve, reject) => {
+            Taro.cropImage({
+              src: file.url ?? '',
+              cropScale: '1:1',
+              success: resolve,
+              fail: reject
+            })
+          })
+          
+          // 更新临时文件状态
+          tempFile.percentage = 50
+          tempFile.message = '上传中'
+          
+          // 上传裁剪后的图片
+          const uploadTask = Taro.uploadFile({
+            url: `${baseBaseUrl}/uploaders?scene=shop`,
+            filePath: (cropResult as any).tempFilePath,
+            name: 'file',
+            success: (res) => {
+              const uploadResult = JSON.parse(res.data)
+              // 移除临时文件
+              goodsImages.value = goodsImages.value.filter(item => item.uid !== tempFile.uid)
+              // 添加上传成功的文件
+              goodsImages.value.push({
+                url: uploadResult.url,
+                uid: file.uid,
+                status: 'success',
+                message: '上传成功',
+                percentage: 100,
+                type: 'image'
+              })
+            },
+            fail: (error) => {
+              console.error('上传失败:', error)
+              tempFile.status = 'error'
+              tempFile.message = '上传失败'
+              tempFile.percentage = 0
+            }
+          })
+          
+          // 监听上传进度
+          uploadTask.progress((res) => {
+            tempFile.percentage = 50 + (res.progress / 2) // 50-100%的进度
+          })
+          
+        } catch (error) {
+          console.error('处理失败:', error)
+          goodsImages.value = goodsImages.value.filter(item => item.uid !== file.uid)
+          goodsImages.value.push({
+            ...file,
+            status: 'error',
+            message: '处理失败',
+            percentage: 0
+          })
+        }
+    }
+
+    // 图片上传成功回调
+    const uploadSucces=({data,option,fileItem})=>{
+        const uploadResult = JSON.parse(data.data)
+        console.log('uploadSucces',uploadResult,option,fileItem)
+        goodsImages.value=goodsImages.value.filter(item=>item.uid!==fileItem.uid)
+        goodsImages.value.push({
+            url: uploadResult.url,
+            uid: fileItem.uid,
+            status: 'success',
+            message: '上传成功',
+            percentage: "100",
+            type: 'image'
+        })
+    }
+
+    // 删除SKU
+    const removeSku=(key)=>{
+        if(formData.sku_list){
+            formData.sku_list= formData.sku_list.filter(item=>item.key!==key)
+        }
+    }
+
+    // 失去焦点校验
+    const customBlurValidate = (prop) => {
+        console.log('prop',formRef.value)
+        formRef.value?.validate(prop)
+        formRefSku.value?.validate(prop)
+    }
+
+    // 监听器:处理响应式更新
+    watch(() => formData.category_id, (val) => {
+        // 当分类改变时,加载对应的规格列表
+        loadSpecifications({category_id: val})
+    })
+
+    watch(() => formData.goods_gallery_list, (val) => {
+        // 监听并更新商品图片列表
+        if(val&&val.length>0){
+       goodsImages.value= val?.map(item => {
+            return {
+                url: item.original,
+                uid: item.img_id,
+                status: 'success',
+                message: '上传成功',
+                percentage: "100",
+                type: 'image'
+            }
+        })
+        }
+    })
+
+    watch(() => formData.sku_list, (val) => {
+        // 当SKU列表变化时,更新商品总库存
+        if(val&&val.length>0){
+            console.log(val.reduce((prev,cur)=>Number(prev)+Number(cur.quantity??0),0))
+            formData.quantity= val.reduce((prev,cur)=>Number(prev)+Number(cur.quantity??0),0)
+        }
+    }, {deep: true})
+
+    watchEffect(() => {
+        // 反向赋值已选中规格
+        if(formData.sku_list){
+            specSelectValue.value = formData.sku_list.flatMap(item => item.spec_list).map(p => `${p.spec_id}_${p.spec_value_id}`)
+        }
+    })
+
+    // 生成规格的笛卡尔积组合
+    function cartesianProduct(obj) {
+        const keys = Object.keys(obj);
+        const result: any = [];
+        // 递归生成笛卡尔积
+        function helper(tempArray, index) {
+            if (index === keys.length) {
+                if(tempArray.length>0){
+                result.push(tempArray);
+                }
+                return;
+            }
+            const key = keys[index];
+            const values = obj[key];
+
+            values.forEach(item => {
+                helper([...tempArray, item], index + 1);
+            });
+        }
+
+        helper([], 0);
+
+        return result;
+    }
+
+    // 数组分组方法
+    const groupBy = (array, key) => {
+        return array.reduce((result, currentValue) => {
+            // 根据指定键获取值
+            const groupKey = currentValue[key];
+
+            // 如果 result 中还没有这个键,创建一个空数组
+            if (!result[groupKey]) {
+                result[groupKey] = [];
+            }
+            // 将当前元素添加到对应的组中
+            result[groupKey].push(currentValue);
+
+            return result;
+        }, {});
+    };
+
+    // 初始化加载分类数据
+    const loadCategories = async () => {
+        const categoryList = useCategoryStoreHook().categoryList
+        if(categoryList.length>0){
+            options.value = categoryList
+            return
+        }
+        const catRootId=  useShopStoreHook().currentShop?.shop_type==='2'?0:-1;
+        const {data:categorys} = await getCategoryList(catRootId)
+        options.value=categorys
+    }
+
+    // 初始化加载商品分组数据,如果本地有数据则直接使用本地数据
+   const shopCategoryList = useShopCategoryStoreHook().shopCategoryList
+   if(shopCategoryList.length===0){
+    getShopCatList().then(res=>{
+        shopCatOption.value = res.data
+        useShopCategoryStoreHook().setShopCategoryList(res.data)
+    })
+   }else{
+    shopCatOption.value = shopCategoryList
+   }
+
+    // 暂存商品信息,添加规格
+    const goSetSpec=(category_id:number)=>{
+        redirectTo('/subPackageA/pages/product-spec-add/index',{category_id})
+    }
+
+    // 商品详情图片
+    const introImages=ref<string[]>([])
+    // 选择图片
+    const chooseMedia=()=>{
+        Taro.chooseMedia({
+            count: 1,
+            mediaType: ['image'],
+            sourceType: ['album', 'camera'],
+            success: (res) => {
+                Taro.cropImage({ //裁剪图片
+                    src: res.tempFiles[0].tempFilePath,
+                    cropScale: '9:16', // 裁剪比例
+                    success: (rs) => {
+                        uploadFile({filePath: rs.tempFilePath, scene: 'shop_logo'}) //上传图片
+                            .then(rsd => {
+                                console.log(rsd)
+                                const data = JSON.parse(rsd.data)
+                                introImages.value.push(data.url)
+                            })
+                            .catch(() => {
+
+                            });
+
+                    }
+                })
+            }
+        })
+    }
+
+
+    // 图片排序
+    const overlayIndex = ref<number | null>(null);
+    // 显示图片排序
+    const showOverlay = (index: number) => {
+        console.log(index)
+        overlayIndex.value = index;
+    };
+    // 隐藏图片排序
+    const hideOverlay = () => {
+    overlayIndex.value = null;
+    };
+    // 删除图片
+    const deleteImage = (index: number) => {
+    introImages.value.splice(index, 1);
+        }
+        // 上移图片
+        const moveImageUp = (index: number) => {
+            if (index > 0) {
+                const temp = introImages.value[index];
+            introImages.value[index] = introImages.value[index - 1];
+            introImages.value[index - 1] = temp;
+            overlayIndex.value = index - 1;
+        }
+    };
+    // 下移图片
+    const moveImageDown = (index: number) => {
+    if (index < introImages.value.length - 1) {
+        const temp = introImages.value[index];
+        introImages.value[index] = introImages.value[index + 1];
+        introImages.value[index + 1] = temp;
+        overlayIndex.value = index + 1;
+    }
+    };
+
+    loadCategories();
+
+    return {
+        formData,
+        formRef,
+        formRefSku,
+        customBlurValidate,
+        visible,
+        categoryValue,
+        options,
+        change,
+        shopCatShow,
+        shopCatOption,
+        shopCatValue,
+        fileChange,
+        uploadSucces,
+        shopCatConfirm,
+        publishGood,
+        goodsImages,
+        specChooseVisible,
+        specList,
+        specSelectValue,
+        onOkspecChoose,
+        removeSku,
+        goSetSpec,
+        goodsImagesUploader,
+        chooseMedia,
+        introImages,
+        overlayIndex,
+        showOverlay,
+        hideOverlay,
+        deleteImage,
+        moveImageUp,
+        moveImageDown
+    }
+}

+ 139 - 0
src/hooks/product-spec/useProductSpecAddHook.ts

@@ -0,0 +1,139 @@
+import {ref} from "vue";
+import type {Category, CategorySpec} from "@/types";
+import Taro, {useLoad} from "@tarojs/taro";
+import {addSpecification, updateSpecification} from "@/api/specification";
+import {getCategoryList} from "@/api/category";
+import {findPath} from "@/utils/commonJs";
+import { useShopStoreHook } from "@/store/shopStore";
+
+export function useProductSpecAddHook(){
+    // 控制级联选择器的显示与隐藏
+    const visible = ref(false);
+    // 存储选中的分类ID数组
+    const value = ref<number[]>([]);
+    // 存储分类选项数据
+    const options = ref<Category[]>([])
+    // 显示选中的分类路径文本
+    const categoryText = ref('');
+    // 表单数据对象
+    const formData = ref<CategorySpec>({
+        
+    })
+    // 表单引用
+    const formRef = ref()
+
+    // 自定义表单验证方法
+    const customerValidate = (prop: string) => {
+        formRef.value?.validate(prop)
+    }
+
+    // 级联选择器选择改变时的处理函数
+    const change = (val, pathNodes) => {
+        console.log(val)
+        formData.value.category_id = val[val.length - 1]
+        categoryText.value = pathNodes.map(item => item.text).join('/')
+        customerValidate('category_id')
+    }
+
+    // 保存规格信息
+    const saveSpecification = () => {
+       formRef.value?.validate().then(({valid,errors})=>{
+        if(valid){
+            if (formData.value.spec_id) {
+                updateSpecification(formData.value, formData.value.spec_id).then(res => {
+                    Taro.showToast({
+                        title: '编辑成功',
+                        icon: 'success',
+                        duration: 2000
+                    })
+                    console.log(res)
+                }).catch(err => {
+                    console.log(err)
+                })
+                console.log('编辑')
+            } else {
+                addSpecification(formData.value, formData.value?.category_id as number).then(() => {
+                    Taro.showToast({
+                        title: '添加成功',
+                        icon: 'success',
+                        duration: 2000
+                    })
+                }).catch(() => {
+                })
+            }       
+        }else{
+            Taro.showToast({
+                title: '请检查输入项',
+                icon: 'error'
+            })
+        }
+        
+    }).catch(err=>{
+        console.log(err)
+        })  
+    }
+
+    // 页面加载时的处理函数
+    useLoad(async (option) => {
+        try {
+            // 获取分类列表
+            const catRootId=  useShopStoreHook().currentShop?.shop_type==='2'?0:-1;
+            const {data} = await getCategoryList(catRootId)
+            options.value= data
+        } catch (e) {
+            console.log(e)
+        }
+        if (option?.spec_id) {
+            Taro.setNavigationBarTitle({
+                title: '编辑商品规格'
+            })
+            formData.value.spec_id = Number(option.spec_id)
+            formData.value.spec_name = option.spec_name
+            formData.value.category_id = Number(option.category_id)
+            console.log('options', options.value)
+            const categoryPath = findPath(options.value, Number(option.category_id))
+            value.value = [...categoryPath.map(item => item.category_id)]
+            categoryText.value = categoryPath.map(item => item.name).join('/')
+        }
+        if(option?.category_id){
+            console.log('option',option)
+            const categoryPath = findPath(options.value, Number(option.category_id))
+            value.value = [...categoryPath.map(item => item.category_id)]
+            categoryText.value = categoryPath.map(item => item.name).join('/')
+            formData.value.category_id = Number(option.category_id)
+        }
+
+    })
+
+    // 表单验证规则
+    const rules = ref({
+        spec_name: [{required: true, message: '请输入规格名称'}]
+    })
+
+    // 显示级联选择器的处理函数
+    const showCascader = () => {
+       //编辑规格时,不允许修改规格分类
+        if(formData.value?.spec_id){            
+            visible.value = false
+        }else{
+            visible.value = true
+        }
+      
+    }
+   
+    // 返回组件需要的响应式数据和方法
+    return {
+        visible,
+        value,
+        options,
+        categoryText,
+        formData,
+        change,        
+        saveSpecification,
+        formRef,
+        rules,
+        customerValidate,
+        showCascader
+    }
+
+}

+ 149 - 0
src/hooks/product-spec/useProductSpecHook.tsx

@@ -0,0 +1,149 @@
+import {ref} from "vue";
+import {
+    addSpecificationValue, deleteSpecification,
+    deleteSpecificationValue,
+    getSpecificationList,
+    updateSpecificationValue
+} from "@/api/specification";
+import {navigateTo} from "@/utils/commonJs";
+import {useDidShow} from "@tarojs/taro";
+import {type Specification, type SpecValue} from "@/types";
+
+
+export default function useProductSpecHook() {
+
+    // 存储规格列表数据
+    const specs = ref<Specification[]>([])
+    // 操作面板标题
+    const title = ref('选择分类')
+    // 页面显示时加载规格数据
+    useDidShow(() => {
+        loadSpecifications(null);
+    })
+    // 获取并加载规格列表数据
+    const loadSpecifications = (data) => {
+        getSpecificationList(data).then(res => {
+            specs.value = [...res.data]
+            // console.log('specs',specs.value)
+        })
+    }
+     // 规格值操作的提示文本
+     const specValueOperateTip = ref('')
+    // 控制规格值操作弹窗的显示状态
+    const specValueOperateVisible = ref(false)
+    // 取消规格值操作时的处理函数
+    const onCancelSpecValueOperate = () => {
+        currentSpecification.value = null
+    }
+    // 处理规格值的添加或修改操作
+    const onOkSpecValueOperate = () => {
+       if(!currentSpecificationValue.value?.spec_value_id){
+        addSpecificationValue({value_name: specValue.value},currentSpecification.value?.spec_id??0).then(() => {
+            specValue.value = ''
+            loadSpecifications(null)
+        })
+       }else{
+           updateSpecificationValue({...currentSpecificationValue.value,spec_value: specValue.value},currentSpecificationValue.value?.spec_value_id).then(() => {
+               specValue.value = ''
+               loadSpecifications(null)
+           })
+       }
+    }
+    // 打开规格操作菜单
+    const operateSpec = (spec) => {
+        actionSheetShow.value = true
+        currentSpecification.value = spec
+        currentSpecificationValue.value = null
+    }
+
+
+    // 规格值的输入内容
+    const specValue = ref('')
+    // 当前选中的规格值对象
+    const currentSpecificationValue = ref<SpecValue|null>(null)
+    // 当前选中的规格对象
+    const currentSpecification = ref<Specification|null>(null)
+   // 操作菜单的选项配置
+   const  menuItems=ref([
+       {name:'编辑规格'},
+       {name:'删除规格',color:'red'},
+       {name: '添加规格值',color:'rgb(73, 101, 242)'}
+   ]);
+    // 处理操作菜单的选择事件
+    const  menuChoose = (item) => {
+        console.log('menuChoose',item)
+        if(item.name==='添加规格值') {
+            console.log('addSpecValue')
+            specValueOperateTip.value = `${currentSpecification.value?.spec_name}规格添加值`
+            specValueOperateVisible.value = true
+        }else if(item.name==='编辑规格'){
+            navigateTo('/subPackageA/pages/product-spec-add/index',{spec_id:currentSpecification.value?.spec_id,spec_name:currentSpecification.value?.spec_name,category_id:currentSpecification.value?.category_id})
+        }else if(item.name==='删除规格'){
+            removeSpecification(currentSpecification.value)
+        }
+    }
+    // 控制操作菜单的显示状态
+    const actionSheetShow = ref(false)
+    // 处理规格值的编辑操作
+    const editSpecValue= (specVal) => {
+      currentSpecificationValue.value=specVal
+        specValueOperateTip.value = `修改规格值`
+        specValue.value=specVal?.spec_value
+        specValueOperateVisible.value = true
+    }
+    // 删除操作的确认提示文本
+    const deleteTipContent=ref('')
+    // 控制删除确认对话框的显示状态
+    const  deteleDialogVisible=ref(false)
+    // 处理删除操作的取消事件
+    const onCancelDelete=()=>{}
+    // 处理规格或规格值的删除操作
+    const onOkDelete=()=>{
+        if(currentSpecificationValue.value?.spec_value_id){
+            deleteSpecificationValue(currentSpecificationValue.value?.spec_value_id).then(()=>{
+                loadSpecifications(null)
+            })
+        }
+        if(currentSpecification.value?.spec_id){
+            deleteSpecification(currentSpecification.value?.spec_id).then(()=>{
+                loadSpecifications(null)
+            })
+        }
+    }
+    // 处理规格值的删除事件
+    const removeSpecificationValue=(specVal)=>{
+        currentSpecificationValue.value=specVal
+        currentSpecification.value=null
+        deleteTipContent.value=`确定删除${specVal.spec_value}吗?`
+        deteleDialogVisible.value=true
+    }
+    // 处理规格的删除事件
+    const removeSpecification=(spec)=>{
+        currentSpecification.value=spec
+        currentSpecificationValue.value=null
+        deleteTipContent.value=`确定删除${spec.spec_name}吗?`
+        deteleDialogVisible.value=true
+    }
+
+
+    return {
+        title,
+        specs,
+        specValueOperateTip,
+        specValueOperateVisible,
+        onCancelSpecValueOperate,
+        onOkSpecValueOperate,
+        operateSpec,
+        specValue,
+        actionSheetShow,
+        menuItems,
+        menuChoose,
+        editSpecValue,
+        deleteTipContent,
+        deteleDialogVisible,
+        onCancelDelete,
+        onOkDelete,
+        removeSpecificationValue,
+        loadSpecifications
+    }
+}

+ 76 - 0
src/hooks/product-spec/useSpecSearchHook.ts

@@ -0,0 +1,76 @@
+import {ref} from "vue";
+import type {Category} from "@nutui/icons-vue-taro";
+import {getCategoryList} from "@/api/category";
+import { useShopStoreHook } from "@/store/shopStore";
+/*
+loadSpecifications 加载规格列表方法
+ */
+export default function useSpecSearchHook(loadSpecifications:(param:any)=>void) {
+
+    //分类选择器显示标记
+    const show = ref(false)
+    //分类选择器值
+    const value = ref<string[]>([])
+    //分类选择器选项
+    const options = ref<Category[]>([])
+    //搜索框值
+    const queryStr = ref('')
+    //分类id
+    const queryCatId = ref(0)
+    //分类选择器改变事件
+    const change = (val) => {
+        queryCatId.value = val[val.length - 1]
+        loadSpecifications({category_id: queryCatId.value, specification_name: queryStr.value})
+        show.value = false
+    }
+    //重置分类id
+    const resetCatId = () => {
+        queryCatId.value = 0
+       loadSpecifications({category_id: null, specification_name: queryStr.value})
+        show.value = false
+    }
+    //搜索
+    const search = () => {
+        // if (queryStr.value === '') {
+        //     Taro.showToast({title: '请输入搜索内容', icon: 'none'})
+        //     return
+        // }
+        const queryParam:{category_id?:number,specification_name?:string} = {category_id: queryCatId.value}
+        if(queryStr.value!==''){
+            queryParam.specification_name = queryStr.value
+        }
+        if(queryCatId.value===0){
+            delete queryParam.category_id
+        }
+        loadSpecifications(queryParam)
+         show.value = false
+    }
+    //分类选择器路径改变事件
+    const pathChange = (...args: any[]) => {
+        console.log('pathChange', ...args)
+
+    }
+    //加载分类
+    const loadCategories = () => {
+        const catRootId=  useShopStoreHook().currentShop?.shop_type==='2'?0:-1;
+        getCategoryList(catRootId).then(res => {
+            options.value = [...res.data]
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+    //调用加载分类方法
+    loadCategories()
+
+    return {
+        show,
+        value,
+        options,
+        queryStr,
+        queryCatId,
+        change,
+        resetCatId,
+        search,
+        pathChange
+    }
+}

+ 148 - 0
src/hooks/scan-receipt/useScanReceiptRecordHook.ts

@@ -0,0 +1,148 @@
+import { scanReceiptDailyCount, scanReceiptRecord, scanReceiptRefund } from "@/api/receipt"
+import { useShopStoreHook } from "@/store/shopStore"
+import type { PaymentBillDO, PaymentBillParam } from "@/types"
+import Taro from "@tarojs/taro"
+import { usePullDownRefresh, useReachBottom } from "@tarojs/taro"
+import dayjs from "dayjs"
+import { ref, watch } from "vue"
+
+export default function useScanReceiptRecordHook() {
+
+    const queryParam = ref<PaymentBillParam>({
+        page_no: 1,
+        page_size: 10,
+        seller_id: useShopStoreHook().currentShop.shop_id,
+        create_time: dayjs().startOf('day').unix(),
+        trade_type: 'none',
+        is_pay: true
+    })
+
+    const calendarShow = ref(false)
+    const date = ref<string>(dayjs().format('YYYY-MM-DD'))
+    const startDate = ref<string>(dayjs().add(-1, 'year').format('YYYY-MM-DD'))
+    const selectDate = (date: Date[]) => {
+        console.log(date)
+    }
+
+    const chooseDate = (param) => {
+        console.log(param[3])
+        queryParam.value.create_time = dayjs(param[3]).unix()
+        calendarShow.value = false
+        queryParam.value.page_no = 1
+        loadRecords()
+        loadDailyCount()
+    }
+
+    const resetDate = () => {
+        date.value = dayjs().format('YYYY-MM-DD')
+        queryParam.value.create_time = undefined
+        calendarShow.value = false
+        queryParam.value.page_no = 1
+    }
+    // 日历引用
+    const calendarRef = ref()
+    const dateMenuRef = ref()
+    // 监听日历显示状态
+    watch(() => calendarShow.value, (newVal) => {
+        console.log('calendarShow', newVal)
+        dateMenuRef.value.toggle(newVal)
+    })
+
+    const loadFinish = ref(false)
+    const records = ref<PaymentBillDO[]>([])
+    const loadRecords = () => {
+        scanReceiptRecord(queryParam.value).then(res => {
+            if (queryParam.value.page_no === 1) {
+                records.value = res.data.data
+            } else {
+                records.value = [...records.value, ...res.data.data]
+            }     
+            if(res.data.data.length < queryParam.value.page_size){
+                loadFinish.value = true
+            }
+            Taro.hideNavigationBarLoading();
+            Taro.stopPullDownRefresh();
+        })
+    }
+
+    const dailyCount = ref<any>()
+    const loadDailyCount = () => {
+        scanReceiptDailyCount({create_time: queryParam.value.create_time, seller_id: useShopStoreHook().currentShop.shop_id}).then(res => {
+            dailyCount.value = res.data
+        })
+    }
+
+    // 上拉加载更多
+    useReachBottom(() => {
+        if (!loadFinish.value) {
+            queryParam.value.page_no += 1
+            loadRecords()
+        }
+    })
+
+    // 下拉刷新
+    usePullDownRefresh(() => {
+        queryParam.value.page_no = 1
+        loadFinish.value = false
+        Taro.showNavigationBarLoading()
+        loadRecords()
+        
+    })
+
+    const deleteTipContent = ref('')
+    const deteleDialogVisible = ref(false)
+    const onCancelDelete = () => {
+        deteleDialogVisible.value = false
+    }
+    const onOkDelete = () => {
+        deteleDialogVisible.value = false
+        if(willRefundBillId.value){
+            scanReceiptRefund(willRefundBillId.value).then(res => {
+                Taro.showToast({
+                    title: '退款成功',
+                    icon: 'success',
+                    duration: 2000,
+                    complete: () => {
+                        setTimeout(() => {
+                            loadRecords()
+                            loadDailyCount()
+                        }, 2000)
+                    }
+                })
+            }).catch(err => {
+                Taro.showToast({
+                    title: '退款失败',
+                    icon: 'none',
+                    duration: 2000
+                })  
+            }).finally(() => {
+                willRefundBillId.value = undefined
+            })
+        }
+    }
+    const willRefundBillId = ref<number|undefined>()
+    const refund = (bill: PaymentBillDO) => {
+        willRefundBillId.value =bill.bill_id
+        deteleDialogVisible.value = true
+        deleteTipContent.value = `确定要对[${bill.out_trade_no}]退款吗?`
+    }
+    loadRecords()
+    loadDailyCount()
+    return {
+        queryParam,
+        calendarShow,
+        date,
+        startDate,
+        dateMenuRef,
+        records,
+        deleteTipContent,
+        deteleDialogVisible,
+        onCancelDelete,
+        onOkDelete,
+        selectDate,
+        chooseDate,
+        resetDate,
+        dailyCount,
+        refund
+    }
+}

+ 255 - 0
src/hooks/shop-apply/useShopApplyHook.ts

@@ -0,0 +1,255 @@
+import { uploadFile } from "@/api/file";
+import { getChildRegions } from "@/api/regions";
+import type { O2OShopVO } from "@/types";
+import Taro from "@tarojs/taro";
+import { nextTick, ref } from "vue";
+import { buyerApplyNewShop, sellerApplyNewShop } from "@/api/shop";
+import { useUserStoreHook } from "@/store/user";
+import { loginByMemberId, switchShop } from "@/api/passport";
+import { useShopStoreHook } from "@/store/shopStore";
+
+
+export default function useShopApplyHook() {
+    //表单数据
+    const formData = ref<O2OShopVO>({
+        shop_name: "",
+        link_name: "",
+        link_phone: "",
+        shop_add: "",
+        bank_name: "",
+        bank_account_name: "",
+        bank_number: "",
+        start_time: "07:00:00",
+        end_time: "23:00:00"
+    });
+    //表单ref
+    const formRef = ref()
+    // 地区选择相关的响应式变量
+    const regionValue = ref<number[]>([])
+    const visible = ref(false)
+    const show = ref(false);
+    const regionText = ref('')
+
+    // 初始化地区文本和值
+    regionText.value = `${formData.value.shop_province ?? ''}${formData.value.shop_city ?? ''}${formData.value.shop_county ?? ''}${formData.value.shop_town ?? ''}`
+    regionValue.value = [formData.value.shop_province_id ?? 0, formData.value.shop_city_id ?? 0, ...(formData.value.shop_county_id ? [formData.value.shop_county_id] : []), ...(formData.value.shop_town_id ? [formData.value.shop_town_id] : [])]
+
+    // 地区选择变化处理函数
+    const change = (val:number[], pathNodes:any[]) => {
+        regionText.value = pathNodes.map(item => item.text).join('')
+        formData.value.shop_region=val[val.length - 1]
+       nextTick(()=>{
+        customBlurValidate('shop_region')
+       })
+        console.log('change', pathNodes, val)
+    }
+
+    const pathChange = (val) => {
+        console.log('pathChange', val)
+    }
+
+
+    // 懒加载地区数据
+    const lazyLoad = (node, resolve) => {
+        if (node.root) {
+            getChildRegions(0).then(res => {
+                const result = res.data.map(item => {
+                    return {
+                        text: item.local_name,
+                        value: item.id,
+                        leaf: item.bool_leaf,
+                        ...item
+                    }
+                })
+                console.log('result', result)
+                resolve(result)
+            })
+        } else {
+            getChildRegions(node.value).then(res => {
+                const result = res.data.map(item => {
+                    return {
+                        text: item.local_name,
+                        value: item.id,
+                        leaf: item.bool_leaf,
+                        ...item
+                    }
+                })
+                resolve(result)
+            })
+        }
+    }
+
+    // 选择店铺logo
+    const chooseMedia = () => {
+        Taro.chooseMedia({
+            count: 1,
+            mediaType: ['image'],
+            sourceType: ['album', 'camera'],
+            maxDuration: 30,
+            camera: 'back',
+            success: (res) => { //选择图片
+                Taro.cropImage({ //裁剪图片
+                    src: res.tempFiles[0].tempFilePath,
+                    cropScale: '1:1', // 裁剪比例
+                    success: (rs) => {
+                        uploadFile({ filePath: rs.tempFilePath, scene: 'shop_logo' }) //上传图片
+                            .then(rsd => {
+                                console.log(rsd)
+                                const data = JSON.parse(rsd.data)
+                                formData.value.shop_logo = data.url;
+                                customBlurValidate('shop_logo')
+                            })
+                            .catch(() => {
+
+                            });
+
+                    }
+                })
+                console.log(res.tempFiles)
+                console.log(res.type)
+            }
+        })
+    }
+
+    //失去焦点校验
+    const customBlurValidate = (prop: string) => {
+        formRef.value?.validate(prop)
+    }
+
+
+    // 选择店铺横幅
+    const chooseShopBanner = () => {
+        Taro.chooseMedia({
+            count: 1,
+            mediaType: ['image'],
+            sourceType: ['album', 'camera'],
+            maxDuration: 30,
+            camera: 'back',
+            success: (res) => {
+                Taro.cropImage({
+                    src: res.tempFiles[0].tempFilePath,
+                    cropScale: '16:9', // 裁剪比例
+                    success: (res) => {
+                        console.log(res.tempFilePath)
+                        uploadFile({ filePath: res.tempFilePath, scene: 'shop_banner' })
+                            .then(rs => {
+                                console.log(rs)
+                                const data = JSON.parse(rs.data)
+                                formData.value.shop_banner = data.url;
+                                // message("更新横幅成功", { type: "success" });
+                            })
+                            .catch(() => {
+                                // message(`提交异常 ${error}`, { type: "error" });
+                            });
+                    }
+                })
+            }
+        })
+    }
+
+    // 选择营业执照图片
+    const chooseLicenceImg = () => {
+        Taro.chooseMedia({
+            count: 1,
+            mediaType: ['image'],
+            sourceType: ['album', 'camera'],
+            maxDuration: 30,
+            camera: 'back',
+            success: (res) => {
+                Taro.cropImage({
+                    src: res.tempFiles[0].tempFilePath,
+                    cropScale: '16:9', // 裁剪比例
+                    success: (res) => {
+                        console.log(res.tempFilePath)
+                        uploadFile({ filePath: res.tempFilePath, scene: 'licence_img' })
+                            .then(rs => {
+                                console.log(rs)
+                                const data = JSON.parse(rs.data)
+                                formData.value.licence_img = data.url;
+                                // message("更新横幅成功", { type: "success" });
+                            })
+                            .catch(() => {
+                                // message(`提交异常 ${error}`, { type: "error" });
+                            });
+                    }
+                })
+            }
+        })
+    }
+    //当前登陆用户信息
+    const userInfo = useUserStoreHook().user;
+
+    // 提交表单
+    const onSubmit = () => {
+        formRef.value?.validate().then(({valid}) => {
+            if (valid) {
+                console.log('useinfo',userInfo)
+                if (userInfo.bool_seller) {
+                    sellerApplyNewShop(formData.value).then(rs => {
+                        //提示店铺申请成功
+                        Taro.showToast({
+                            title: '店铺申请成功',
+                            icon: 'success',
+                            duration: 2000
+                        });
+                        //切换店铺到新店铺
+                        switchShop(rs.data.shop_id).then(async res=>{
+                            useUserStoreHook().setUserInfo({...res.data,bool_seller:true})
+                            useUserStoreHook().setToken({...res.data})
+                             await useShopStoreHook().getShops()
+                         })
+                    }).catch(err => {
+                        console.log(err)
+                    })
+                } else {
+                    buyerApplyNewShop(formData.value).then(rs => {
+                        //提示店铺申请成功
+                        Taro.showToast({
+                            title: '店铺申请成功',
+                            icon: 'success',
+                            duration: 2000
+                        });
+                        //通过memberId登陆
+                        loginByMemberId(rs.data.member_id).then(async res=>{
+                            await useUserStoreHook().setUserInfo({...res.data,bool_seller:true})
+                        //     //切换店铺到新店铺
+                        switchShop(rs.data.shop_id).then(async res=>{
+                             await useUserStoreHook().setUserInfo({...res.data,bool_seller:true})
+                             await useShopStoreHook().getShops()
+                         })
+                        })
+                        
+                    })
+                }
+            }else{
+                Taro.showToast({
+                    title: '请填写完整信息',
+                    icon: 'none',
+                    duration: 2000
+                });
+            }
+        })
+    }
+
+
+    return {
+        formData,
+        formRef,
+        regionValue,
+        visible,
+        show,
+        regionText,
+        change,
+        pathChange,
+        chooseMedia,
+        lazyLoad,
+        customBlurValidate,
+        chooseShopBanner,
+        chooseLicenceImg,
+        onSubmit
+    }
+
+}
+
+
+

+ 226 - 0
src/hooks/shop-info/useShopInfoHook.tsx

@@ -0,0 +1,226 @@
+import {computed, reactive, Ref, ref} from "vue";
+import {useShopStoreHook} from "@/store/shopStore";
+import {getChildRegions} from "@/api/regions";
+import Taro from "@tarojs/taro";
+import {uploadFile} from "@/api/file";
+import dayjs from "dayjs";
+import customParseFormat from "dayjs/plugin/customParseFormat";
+import {updateShopInfo} from "@/api/shop";
+
+dayjs.extend(customParseFormat)
+
+export default function useShopInfoHook() {
+    // 获取当前商店信息
+    const currentShop = computed(() => useShopStoreHook().currentShop);
+
+    // 创建响应式表单数据
+    const formData = reactive({...currentShop.value});
+
+    // 地区选择相关的响应式变量
+    const regionValue = ref<any[]>([])
+    const visible = ref(false)
+    const show = ref(false);
+    const regionText = ref('')
+    
+    // 初始化地区文本和值
+    regionText.value = `${formData.shop_province ?? ''}${formData.shop_city ?? ''}${formData.shop_county ?? ''}${formData.shop_town ?? ''}`
+    regionValue.value = [formData.shop_province_id, formData.shop_city_id, ...(formData.shop_county_id ? [formData.shop_county_id] : []), ...(formData.shop_town_id ? [formData.shop_town_id] : [])]
+    
+    // 地区选择变化处理函数
+    const change = (val, pathNodes) => {
+        regionText.value = pathNodes.map(item => item.text).join('')
+    
+        console.log('change', pathNodes, val)
+    }
+
+    const pathChange = (val) => {
+        console.log('pathChange', val)
+    }
+
+    // 懒加载地区数据
+    const lazyLoad = (node, resolve) => {
+        if (node.root) {
+            getChildRegions(0).then(res => {
+                const result = res.data.map(item => {
+                    return {
+                        text: item.local_name,
+                        value: item.id,
+                        leaf: item.bool_leaf,
+                        ...item
+                    }
+                })
+                console.log('result', result)
+                resolve(result)
+            })
+        } else {
+            getChildRegions(node.value).then(res => {
+                const result = res.data.map(item => {
+                    return {
+                        text: item.local_name,
+                        value: item.id,
+                        leaf: item.bool_leaf,
+                        ...item
+                    }
+                })
+                resolve(result)
+            })
+        }
+    }
+
+    // 选择店铺logo
+    const chooseMedia = () => {
+        Taro.chooseMedia({
+            count: 1,
+            mediaType: ['image'],
+            sourceType: ['album', 'camera'],
+            maxDuration: 30,
+            camera: 'back',
+            success: (res) => { //选择图片
+                Taro.cropImage({ //裁剪图片
+                    src: res.tempFiles[0].tempFilePath,
+                    cropScale: '1:1', // 裁剪比例
+                    success: (rs) => {
+                        uploadFile({filePath: rs.tempFilePath, scene: 'shop_logo'}) //上传图片
+                            .then(rsd => {
+                                console.log(rsd)
+                                const data = JSON.parse(rsd.data)
+                                formData.shop_logo = data.url;
+                            })
+                            .catch(() => {
+
+                            });
+
+                    }
+                })
+                console.log(res.tempFiles)
+                console.log(res.type)
+            }
+        })
+    }
+
+    // 选择店铺横幅
+    const chooseShopBanner = () => {
+        Taro.chooseMedia({
+            count: 1,
+            mediaType: ['image'],
+            sourceType: ['album', 'camera'],
+            maxDuration: 30,
+            camera: 'back',
+            success: (res) => {
+                Taro.cropImage({
+                    src: res.tempFiles[0].tempFilePath,
+                    cropScale: '16:9', // 裁剪比例
+                    success: (res) => {
+                        console.log(res.tempFilePath)
+                        uploadFile({filePath: res.tempFilePath, scene: 'shop_banner'})
+                            .then(rs => {
+                                console.log(rs)
+                                const data = JSON.parse(rs.data)
+                                formData.shop_banner = data.url;
+                                // message("更新横幅成功", { type: "success" });
+                            })
+                            .catch(() => {
+                                // message(`提交异常 ${error}`, { type: "error" });
+                            });
+                    }
+                })
+            }
+        })
+    }
+
+    // 营业时间相关的响应式变量
+    const start_time_show = ref(false);
+    const start_time = ref('');
+    const end_time_show = ref(false);
+    const end_time = ref('');
+    const businessDayCheckValue: Ref<string[]|undefined> = ref([])
+    const start_time_val = ref(new Date())
+    const end_time_val = ref(new Date())
+    const business_day:Ref<number[]|undefined> = ref([0, 0, 0, 0, 0, 0, 0])
+
+    // 营业日变更处理函数
+    const businessDayChange = (val) => {
+        business_day.value?.forEach((_item, index) => {
+            if(!business_day.value) return
+            business_day.value[index] = val.includes(index + '') ? 1 : 0
+        })
+    }
+
+    // 开始时间确认处理函数
+    const startTimeConfirm = ({selectedValue}) => {
+        start_time.value = selectedValue.join(':')
+        start_time_show.value = false
+    }
+
+    // 结束时间确认处理函数
+    const endTimeConfirm = ({selectedValue}) => {
+        end_time.value = selectedValue.join(':')
+        end_time_show.value = false
+    }
+
+    // 初始化营业时间和日期
+    const {start_time: startTime, end_time: endTime, business_date} = formData;
+    start_time_val.value = dayjs(startTime, 'HH:mm:ss').toDate();
+    end_time_val.value = dayjs(endTime, 'HH:mm:ss').toDate();
+    start_time.value = dayjs(startTime, 'HH:mm:ss').format('HH:mm');
+    end_time.value = dayjs(endTime, 'HH:mm:ss').format('HH:mm');
+    businessDayCheckValue.value = business_date?.split('').map(((item, index) => parseInt(item) === 1 ? index + '' : '-1'))
+    business_day.value = business_date?.split('').map(item => parseInt(item))
+
+    // 保存店铺信息
+    const saveShopInfo = () => {
+        const {
+            shop_name,
+            link_phone,
+            shop_add,
+            shop_logo,
+            shop_desc,
+            shop_banner
+        } = formData
+        updateShopInfo({
+            shop_name,
+            link_phone,
+            shop_add,
+            shop_logo,
+            shop_desc,
+            shop_banner,
+            business_date: business_day.value?.join(''),
+            start_time: dayjs(start_time.value, 'HH:mm').format('HH:mm:ss'),
+            end_time: dayjs(end_time.value, 'HH:mm').format('HH:mm:ss'),
+            shop_region: regionValue.value[regionValue.value.length - 1],
+        }).then(() => {
+            Taro.showToast({
+                title: '修改成功',
+                icon: 'success',
+                duration: 2000
+            });
+            useShopStoreHook().getShops();
+        })
+    }
+
+    // 返回所有需要在组件中使用的变量和函数
+    return {
+        formData,
+        regionValue,
+        regionText,
+        visible,
+        show,
+        start_time_show,
+        start_time,
+        start_time_val,
+        end_time_show,
+        end_time,
+        end_time_val,
+        businessDayCheckValue,
+        business_day,
+        change,
+        pathChange,
+        lazyLoad,
+        chooseMedia,
+        chooseShopBanner,
+        businessDayChange,
+        startTimeConfirm,
+        endTimeConfirm,
+        saveShopInfo
+    }
+}

+ 74 - 0
src/hooks/shop-time/useShopOpenTimeHook.tsx

@@ -0,0 +1,74 @@
+import {computed, Ref, ref} from "vue";
+import dayjs from "dayjs";
+import customParseFormat from 'dayjs/plugin/customParseFormat'
+import {useShopStoreHook} from "@/store/shopStore";
+import {updateShopStatus} from "@/api/shop";
+import Taro from "@tarojs/taro";
+
+dayjs.extend(customParseFormat)
+
+export function useShopOpenTimeHook() {
+    const start_time_show = ref(false);
+    const start_time = ref('');
+    const end_time_show = ref(false);
+    const end_time = ref('');
+    const businessDayCheckValue: Ref<string[]|undefined> = ref([])
+    const start_time_val = ref(new Date())
+    const end_time_val = ref(new Date())
+    const business_day:Ref<number[]|undefined> = ref([0, 0, 0, 0, 0, 0, 0])
+    const change2 = (val) => {
+        business_day.value?.forEach((_item, index) => {
+            if(!business_day.value) return
+            business_day.value[index] = val.includes(index+'') ? 1 : 0
+        })
+    }
+    const startTimeConfirm = ({selectedValue}) => {
+        start_time.value = selectedValue.join(':')
+        start_time_show.value = false
+    }
+    const endTimeConfirm = ({selectedValue}) => {
+        end_time.value = selectedValue.join(':')
+        end_time_show.value = false
+    }
+
+    const currentShopInfo = computed(() => useShopStoreHook().currentShop);
+    const {start_time: startTime, end_time: endTime,business_date} = currentShopInfo.value;
+    start_time_val.value = dayjs(startTime, 'HH:mm:ss').toDate();
+    end_time_val.value = dayjs(endTime, 'HH:mm:ss').toDate();
+    start_time.value = dayjs(startTime, 'HH:mm:ss').format('HH:mm');
+    end_time.value = dayjs(endTime, 'HH:mm:ss').format('HH:mm');
+    businessDayCheckValue.value = business_date?.split('').map(((item, index) => parseInt(item)===1?index+'':'-1'))
+    business_day.value = business_date?.split('').map(item => parseInt(item))
+    console.log('business_day', business_day.value)
+
+    const saveShopState = () => {
+        updateShopStatus({shopId:currentShopInfo.value.shop_id,
+            businessDate:business_day.value?.join(''),
+            startTime:dayjs(start_time.value, 'HH:mm').format('HH:mm:ss'),
+            endTime:dayjs(end_time.value, 'HH:mm').format('HH:mm:ss'),
+            shopState:currentShopInfo.value.shop_state
+        }).then(res=>{
+            Taro.showToast({
+                title: '修改成功',
+                icon: 'success',
+                duration: 2000
+            });
+            useShopStoreHook().getShops();
+            console.log('updateShopStatus',res)
+        })
+    }
+
+    return {
+        start_time_show,
+        start_time,
+        start_time_val,
+        end_time_show,
+        end_time,
+        end_time_val,
+        businessDayCheckValue,
+        change2,
+        startTimeConfirm,
+        endTimeConfirm,
+        saveShopState
+    }
+}

+ 169 - 0
src/hooks/statement-detail/useStatementDetailHook.ts

@@ -0,0 +1,169 @@
+import {ref} from "vue";
+import {type StatementItemQueryParam, StatementItemVO, StatementVO} from "@/types";
+import  {useLoad, usePullDownRefresh, useReachBottom} from "@tarojs/taro";
+import {getStatementDetail, getStatementItem, handleStatementStatus, updateStatement} from "@/api/statement";
+import Taro from "@tarojs/taro";
+
+export default function useStatementDetailHook() {
+    const formData = ref<StatementVO>({
+        id: 0,
+        payee_shop_id: 0,
+        payee_shop_name: '',
+        statement_no: '',
+        payer_shop_id: 0,
+        payer_shop_name: '',
+        create_time: undefined,
+        statement_start: undefined,
+        statement_end: undefined,
+        payee_modify_time: undefined,
+        payer_modify_time: undefined,
+        payer_status: undefined,
+        payee_status: undefined,
+        status: '',
+        amount: undefined,
+        discount: undefined,
+        actualpay: undefined,
+        payed: undefined,
+        debt: undefined,
+        remark: undefined,
+        payer_operator: '',
+        payee_operator: undefined,
+        vouchers: undefined
+    })
+
+
+    //账单状态
+    const statusOptions = [
+        {label: '待确认', value: '00'},
+        {label: '付款方已确认', value: '10'},
+        {label: '收款方已确认', value: '01'},
+        {label: '双方已确认', value: '11'},
+        {label: '部分支付', value: '21'},
+        {label: '部分收款', value: '23'},
+        {label: '已支付', value: '31'},
+        {label: '已完成', value: '33'}
+    ]
+    //付款方状态
+    const payerStatusOptions = [
+        {label: '待处理', value: 0},
+        {label: '已确认', value: 1},
+        {label: '部分支付', value: 2},
+        {label: '已支付', value: 3},
+    ]
+    //收款方状态
+    const payeeStatusOptions = [
+        {label: '待处理', value: 0},
+        {label: '已确认', value: 1},
+        {label: '确认收款', value: 3},
+    ]
+    //账单明细查询参数
+    const stateItemQueryParams = ref<StatementItemQueryParam>({
+        page_no: 1,
+        page_size: 10,
+        statement_id: 0
+    })
+   //加载完成
+    const loadFinish = ref(false)
+     //账单明细
+    const statementItems = ref<StatementItemVO[]>([])
+
+    //加载账单明细
+    const loadStatementItem = () => {
+        getStatementItem(stateItemQueryParams.value).then(res => {
+            if(stateItemQueryParams.value.page_no === 1) {
+                statementItems.value = res.data.data
+            }else {
+                statementItems.value = [...statementItems.value, ...res.data.data]
+            }
+            if(res.data.data.length<stateItemQueryParams.value.page_size){
+                loadFinish.value = true
+            }
+            console.log(res)
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    //修改账单
+    const saveStatement = () => {
+        if(formData.value.id===undefined) return
+        updateStatement(formData.value.id, formData.value).then(res => {
+            Object.assign(formData.value, res.data)
+            Taro.showToast({
+                title: '修改成功',
+                icon: 'none',
+                duration: 2000
+            })
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    const loadStatementDetail= (id: number) => {
+        getStatementDetail(id).then(res => {
+            formData.value = res.data
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    const decodeStatus =(status:string)=>{
+        return statusOptions.find(item=>item.value === status)?.label
+    }
+
+    const decodePayerStatus =(status:number)=>{
+        return payerStatusOptions.find(item=>item.value === status)?.label
+    }
+
+    const decodePayeeStatus =(status:number)=>{
+        return payeeStatusOptions.find(item=>item.value === status)?.label
+    }
+
+    useReachBottom(()=>{
+        if(!loadFinish.value){
+            stateItemQueryParams.value.page_no++
+            loadStatementItem()
+        }
+    })
+
+    usePullDownRefresh(()=>{
+        stateItemQueryParams.value.page_no = 1
+        loadFinish.value = false
+        loadStatementItem()
+    })
+
+    //页面显示生命周期
+    useLoad(async (option) => {
+        if (option) {
+            console.log(option)
+        }
+        if (option?.id) {
+            loadStatementDetail(option?.id)
+            stateItemQueryParams.value.statement_id = option?.id
+            loadStatementItem()
+            // loadGoodsSku(option?.goods_id)
+        }
+    })
+
+    //处理账单状态
+    const dealStatementStatus = (id?: number, status?: number):void => {
+        console.log('id,status',id,status)
+        if(id===undefined||status===undefined) return
+        handleStatementStatus(id, status).then(res => {
+            console.log(res)
+            loadStatementDetail(id)
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    return {
+        formData,
+        statementItems,
+        decodeStatus,
+        decodePayerStatus,
+        decodePayeeStatus,
+        dealStatementStatus,
+        saveStatement
+    };
+}

+ 165 - 0
src/hooks/statement-query/useStatementQueryHook.ts

@@ -0,0 +1,165 @@
+import {ref, computed, watch, toRaw, ComputedRef} from "vue";
+import {StaffBalanceDetail, StaffBalanceDetailQueryParam} from "@/types/statement";
+import {getStaffBalanceDetail, getStaffBalanceTotal} from "@/api/staffBalance";
+import dayjs from "dayjs";
+import {usePullDownRefresh, useReachBottom} from "@tarojs/taro";
+import Taro from "@tarojs/taro";
+
+export default function useStatementQueryHook(cooperations:ComputedRef<{text:string|null,value:number|null}[]>){
+    // 待结算列表
+    const statements = ref<StaffBalanceDetail[]>([])
+    
+    // 查询参数
+    const queryParam = ref<StaffBalanceDetailQueryParam>({
+        page_no: 1,
+        page_size: 10,
+        status: 0,
+        cooperation_id: 0,
+    })
+    
+    // 未结算总额
+    const amount = ref(0);
+    
+    // 加载完成标志
+    const loadFinish = ref(false)
+
+    // 日期范围
+    const dateRange = ref()
+    // 开始日期(默认为一年前)
+    const startDate = ref(dayjs().add(-1, 'year').format('YYYY-MM-DD'))
+
+    // 加载结算列表
+    const loadStatements = () => {
+        const param = {
+            ...toRaw(queryParam.value),
+            cooperation_id: queryParam.value.cooperation_id == 0 ? null : queryParam.value.cooperation_id
+        }
+        getStaffBalanceDetail(param).then(res => {
+            if (queryParam.value.page_no === 1) {
+                statements.value = res.data.data
+            } else {
+                statements.value = [...statements.value, ...res.data.data]
+            }
+            // statements.value = res.data.data
+            if(res.data.data.length < queryParam.value.page_size){
+                loadFinish.value = true
+            }
+            Taro.hideNavigationBarLoading();
+            Taro.stopPullDownRefresh();
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    // 加载未结算总额
+    const loadAmount = () => {
+        const param = {
+            ...toRaw(queryParam.value),
+            cooperation_id: queryParam.value.cooperation_id == 0 ? null : queryParam.value.cooperation_id
+        }
+        getStaffBalanceTotal(param).then(res => {
+            amount.value = res.data
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    // 当前日期范围
+    const date = ref([dayjs().format('YYYY-MM-DD'), dayjs().format('YYYY-MM-DD')])
+    // 日历显示状态
+    const calendarShow = ref(false)
+
+    // 选择日期
+    const chooseDate = (param) => {
+        console.log('choose', param)
+        queryParam.value.start_date = dayjs(param[0][3]).unix()
+        queryParam.value.end_date = dayjs(param[1][3]).unix()
+        calendarShow.value = false
+        queryParam.value.page_no = 1
+        loadFinish.value = false
+        loadStatements()
+        loadAmount()
+    }
+
+    // 选择日期(暂未使用)
+    const selectDate = () => {
+        // dateRange.value.toggle()
+    }
+
+    // 重置日期
+    const resetDate = () => {
+        date.value = []
+        queryParam.value.start_date = undefined
+        queryParam.value.end_date = undefined
+        calendarShow.value = false
+        queryParam.value.page_no = 1
+        loadFinish.value = false
+        loadStatements()
+        loadAmount()
+    }
+
+    // 日历引用
+    const calendarRef = ref()
+
+    // 监听日历显示状态
+    watch(() => calendarShow.value, (newVal) => {
+        console.log('calendarShow', newVal)
+        dateRange.value.toggle(newVal)
+    })
+
+    // 合作方变更
+    const cooperaterChange = () => {
+        queryParam.value.page_no = 1
+        loadFinish.value = false
+        loadStatements()
+        loadAmount()
+    }
+
+    // 计算当前选中的合作方名称
+    const cooperatorName = computed(() => {
+        const cooperator = cooperations.value.find(item => item.value === queryParam.value.cooperation_id)
+        return cooperator?.text
+
+    })
+
+    // 上拉加载更多
+    useReachBottom(() => {
+        if(!loadFinish.value) {
+            queryParam.value.page_no += 1
+            loadStatements()
+        }
+    })
+
+    // 下拉刷新
+    usePullDownRefresh(() => {
+        Taro.stopPullDownRefresh()
+        queryParam.value.page_no = 1
+        loadFinish.value = false
+        Taro.showNavigationBarLoading()
+        loadStatements()
+       
+    })
+
+    // 初始加载数据
+    loadStatements()
+    loadAmount()
+
+    // 返回所有需要的状态和方法
+    return {
+        statements,
+        queryParam,
+        loadFinish,
+        amount,
+        // cooperations,
+        dateRange,
+        startDate,
+        calendarRef,
+        calendarShow,
+        date,
+        chooseDate,
+        selectDate,
+        resetDate,
+        cooperaterChange,
+        cooperatorName
+    }
+}

+ 119 - 0
src/hooks/statement/useGenerateStatementHook.ts

@@ -0,0 +1,119 @@
+import { addStatement } from "@/api/statement"
+import type { StaffBalanceDetailQueryParam } from "@/types"
+import Taro from "@tarojs/taro"
+import dayjs from "dayjs"
+import { ref } from "vue"
+
+export default function useGenerateStatementHook() {
+
+    // 控制合作方选择器的可见性
+    const showCoopPicker = ref(false)
+    // 控制日历选择器的可见性
+    const calendarShow = ref(false)   
+    // 当前选中的合作方信息
+    const selectedCoop = ref<{ text?: string, value?: number }>()
+    // 将日期转换为 YYYY-MM 格式
+    const formatDate = (date: string) => {
+        return dayjs(date).format('YYYY-MM')
+    }
+    // 表单实例引用
+    const formRef = ref()
+    // 表单数据对象
+    const formData = ref<StaffBalanceDetailQueryParam>({
+        status: 0,      
+    })
+    // 合作方选择器确认
+    const onCoopConfirm = ({ selectedValue, selectedOptions }:any) => {
+        formData.value.cooperation_id = selectedValue[0] as number
+        selectedCoop.value = selectedOptions[0] as { text?: string, value?: number }
+        showCoopPicker.value = false
+        customValidate('cooperation_id')
+    }
+
+
+    // 提交表单
+    const handleSubmit = () => {
+        console.log(formData.value)
+        formRef.value.validate().then(({valid, errors}) => {
+            if(valid) {
+              addStatement(formData.value,formData.value.cooperation_id as number).then(res => {
+                Taro.showToast({
+                    title: '生成账单成功',
+                    icon: 'success',
+                    duration: 2000
+                })
+              }).catch(err => {
+                Taro.showToast({
+                    title: `${err.data.message}`,
+                    icon: 'none',
+                    duration: 2000
+                })
+              })
+            }else{
+                Taro.showToast({
+                    title: '请检查表单填写是否正确',
+                    icon: 'none',
+                    duration: 2000
+                })
+            }
+        })
+    }
+    // 表单字段单独校验
+    const customValidate = (prop: string) => {
+        return formRef.value.validate(prop)
+    }
+
+    // 日历选择器实例引用
+    const calendarRef = ref()
+    // 日期选择的起始可选日期
+    const startDate = ref(dayjs().add(-1, 'year').format('YYYY-MM-DD'))
+    // 当前选中的日期范围
+    const date = ref([dayjs().format('YYYY-MM-DD'), dayjs().format('YYYY-MM-DD')])
+
+    // 日期范围选择回调
+    const chooseDate = (param) => {
+        console.log('choose', param)
+        formData.value.start_date = dayjs(param[0][3]).unix()
+        formData.value.end_date = dayjs(param[1][3]).unix()
+        calendarShow.value = false
+    }
+    // 清空已选择的日期范围
+    const resetDate = () => {
+        formData.value.start_date = undefined
+        formData.value.end_date = undefined
+        date.value = []
+        calendarShow.value = false
+    }
+
+    // 快速选择上个月日期范围
+    const lastMonth = () => {
+        date.value = [dayjs().startOf('month').add(-1, 'month').format('YYYY-MM-DD'), dayjs().endOf('month').add(-1, 'month').format('YYYY-MM-DD')]
+    }
+    // 快速选择当月日期范围
+    const currentMonth = () => {
+        date.value = [dayjs().startOf('month').format('YYYY-MM-DD'), dayjs().endOf('month').format('YYYY-MM-DD')]
+    }
+    // 快速选择当年日期范围
+    const currentYear = () => {
+        date.value = [dayjs().startOf('year').format('YYYY-MM-DD'), dayjs().endOf('year').format('YYYY-MM-DD')]
+    }
+
+    return {
+        showCoopPicker,
+        calendarShow,      
+        formatDate,
+        handleSubmit,
+        onCoopConfirm,      
+        formData,
+        selectedCoop,
+        formRef,
+        calendarRef,
+        startDate,
+        date,
+        chooseDate,
+        resetDate,
+        lastMonth,
+        currentMonth,
+        currentYear
+    }
+}

+ 104 - 0
src/hooks/statement/useStatementHook.tsx

@@ -0,0 +1,104 @@
+import {ref, toRaw} from "vue";
+import {StatementQueryParam, StatementVO} from "@/types";
+import {getStatementList} from "@/api/statement";
+import {Tag} from "@nutui/nutui-taro";
+import Taro, {usePullDownRefresh, useReachBottom} from "@tarojs/taro";
+
+export default function useStatementHook() {
+
+    // 查询参数
+    const queryParam = ref<StatementQueryParam>({
+        page_no: 1,
+        page_size: 10,
+        status: '99',
+        cooperation_id: 0
+    })
+    // 账单状态选项
+    const statusOptions = [
+        {text: '所有状态', value: '99',color:'blue'},
+        {text: '待确认', value: '00',color: 'red'},
+        {text: '付款方已确认', value: '10',color: 'orange'},
+        {text: '收款方已确认', value: '01',color: 'orange'},
+        {text: '双方已确认', value: '11',color: 'orange'},
+        {text: '部分支付', value: '21',color: 'green'},
+        {text: '部分收款', value: '23',color: 'green'},
+        {text: '已支付', value: '31',color: 'green'},
+        {text: '已完成', value: '33',color: 'green'}
+    ]
+
+    // 是否加载完成
+    const loadFinish = ref(false)
+
+    // 账单列表
+    const statements = ref<StatementVO[]>([])
+
+    // 加载账单列表
+    const loadStatements = () => {
+        const param = {
+            ...toRaw(queryParam.value),
+            status: queryParam.value.status == '99' ? '' : queryParam.value.status,
+            cooperation_id: queryParam.value.cooperation_id == 0 ? null : queryParam.value.cooperation_id
+        }
+        getStatementList(param).then(res => {
+            if(queryParam.value.page_no === 1) {
+                statements.value = res.data.data
+            }else {
+                statements.value = [...statements.value, ...res.data.data]
+            }
+           if(res.data.data.length<queryParam.value.page_size){
+               loadFinish.value = true
+           }
+            Taro.hideNavigationBarLoading()
+            Taro.stopPullDownRefresh()
+        }).catch(err => {
+            Taro.hideNavigationBarLoading()
+            console.log(err)
+        })
+    }
+
+    // 查询条件变化时重新加载
+    const queryChange=()=>{
+        queryParam.value.page_no = 1
+        loadFinish.value = false
+        loadStatements()
+    }
+
+    // 解码状态,返回对应的Tag组件
+    const decodeStatus =(status:string)=>{
+       const sta = statusOptions.find(item=>item.value === status)
+        return <Tag color={sta?.color} >{sta?.text}</Tag>
+    }
+
+    // 上拉加载更多
+    useReachBottom(()=>{
+        if(!loadFinish.value){
+            queryParam.value.page_no++
+            loadStatements()
+        }
+    })
+
+    // 下拉刷新
+    usePullDownRefresh(()=>{
+        Taro.showNavigationBarLoading()
+        queryParam.value.page_no = 1
+        loadFinish.value = false
+        loadStatements()
+    })
+
+    // 初始加载
+    loadStatements()
+
+   // 账单月份
+    const yearMonth = ref<Date>(new Date());
+    
+    // 返回需要的状态和方法
+    return {
+        queryParam,
+        statusOptions,
+        statements,
+        decodeStatus,
+        queryChange,
+        yearMonth
+    }
+
+}

+ 284 - 0
src/hooks/statistics/useStatisticsHook.ts

@@ -0,0 +1,284 @@
+import {ref, watch} from 'vue';
+import type {Ref} from 'vue';
+import type {StatisticsData} from '@/types/statistics';
+import {getStatisticsReport} from "@/api/statistics";
+import dayjs from "dayjs";
+import * as echarts from '@/subPackageB/components/ec-canvas/echarts.min.js'
+
+export default function useStatisticsHook() {
+    // 统计数据,使用ref创建响应式引用
+    const statisticsData: Ref<StatisticsData | null> = ref(null);
+
+    // 年份月份选择,默认为当前日期
+    const yearMonth = ref<Date>(new Date());
+    console.log('yearMonth', yearMonth.value)
+
+    // 获取统计数据的异步函数
+    const getStatisticsData = async () => {
+        getStatisticsReport(dayjs(yearMonth.value).year(),
+            dayjs(yearMonth.value).month()+1).then(res => {
+            statisticsData.value = res.data;
+            console.log('statisticsData', statisticsData.value)
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    // 日期选择器显示状态
+    const datePickerShow = ref<boolean>(false);
+
+    // 日期选择确认回调函数
+    const confirm = ({selectedValue,selectedOptions}) => {
+        console.log('selectedValue', selectedValue, selectedOptions)
+        yearMonth.value = new Date(`${selectedValue[0]}-${selectedValue[1]}`);
+        datePickerShow.value = false;
+
+        getStatisticsData(); // 选择日期后重新获取数据
+    }
+
+    // 日期MenuItem的ref
+    const dateRef = ref()
+
+    // 监听日期选择器显示状态变化
+    watch(() => datePickerShow.value, (newVal) => {
+        console.log('yearMonth', newVal)
+        dateRef.value.toggle(newVal)
+    })
+
+    // 初始化时获取统计数据
+    getStatisticsData();
+      
+    // 初始化频次图表
+    const initChart = (canvas, width, height, dpr) => {
+        // 创建ECharts实例
+        const chart = echarts.init(canvas, null, {
+          width: width,
+          height: height,
+          devicePixelRatio: dpr
+        })
+        canvas.setChart(chart)
+        
+        // 更新图表选项的函数
+        const updateOption = () => {
+          const option = {
+            tooltip: {
+              trigger: 'axis',
+              axisPointer: {
+                type: 'shadow'
+              }
+            },
+            color: ['#1BB723', '#ffab0d'],
+            legend: {
+              bottom: 10,
+              left: 'center',
+            },
+            grid: {
+              left: '3%',
+              right: '4%',
+              top: '3%',
+              bottom: '20%',
+              containLabel: true
+            },
+            xAxis: [
+              {
+                type: 'category',
+                data: ['1次', '2-5次', '5次以上']
+              }
+            ],
+            yAxis: [
+              {
+                type: 'value'
+              }
+            ],
+            series: [
+              {
+                name: '新顾客',
+                type: 'bar',
+                stack: 'ad',
+                emphasis: {
+                  focus: 'series'
+                },
+                data: statisticsData.value?.purchase_frequency.new_customers || []
+              },
+              {
+                name: '老顾客',
+                type: 'bar',
+                stack: 'ad',
+                emphasis: {
+                  focus: 'series'
+                },
+                data: statisticsData.value?.purchase_frequency.old_customers || []
+              }
+            ]
+          }   
+          chart.setOption(option)
+        }
+      
+        // 首次调用更新函数
+        updateOption()
+      
+        // 监听统计数据变化,更新图表
+        watch(() => statisticsData.value?.purchase_frequency, updateOption, { deep: true })
+      
+        return chart
+    }
+      
+    // 初始化趋势图表
+    const initTrendChart = (canvas, width, height, dpr) => {
+        // 创建ECharts实例
+        const chart = echarts.init(canvas, null, {
+          width: width,
+          height: height,
+          devicePixelRatio: dpr
+        })
+        canvas.setChart(chart)
+        
+        // 更新图表选项的函数
+        const updateOption = () => {
+          const option = {
+            color: ['#01EC0D', '#EAC13D', '#115BE3'],
+            tooltip: {
+              trigger: 'axis',
+              axisPointer: {
+                type: 'cross',
+                label: {
+                  backgroundColor: '#6a7985'
+                }
+              }
+            },
+            legend: {
+              data: ['交易金额', '交易笔数', '交易人数'],
+              bottom: 10,
+              left: 'center',
+            },
+            toolbox: {
+              feature: {
+                saveAsImage: {}
+              }
+            },
+            grid: {
+              left: '3%',
+              right: '4%',
+              top: '3%',
+              bottom: '20%',
+              containLabel: true
+            },
+            xAxis: [
+              {
+                type: 'category',
+                boundaryGap: false,
+                data: statisticsData.value?.data_trend.dates || []
+              }
+            ],
+            yAxis: [
+              {
+                type: 'value'
+              }
+            ],
+            series: [
+              {
+                name: '交易金额',
+                type: 'line',
+                stack: 'Total',
+                smooth: true,
+                lineStyle: {
+                  width: 0
+                },
+                showSymbol: false,
+                areaStyle: {
+                  opacity: 0.8,
+                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                    {
+                      offset: 0,
+                      color: 'rgb(128, 255, 165)'
+                    },
+                    {
+                      offset: 1,
+                      color: 'rgb(1,236,13)'
+                    }
+                  ])
+                },
+                emphasis: {
+                  focus: 'series'
+                },
+                data: statisticsData.value?.data_trend.transaction_amounts || []
+              },
+              {
+                name: '交易笔数',
+                type: 'line',
+                stack: 'Total',
+                smooth: true,
+                lineStyle: {
+                  width: 0
+                },
+                showSymbol: false,
+                areaStyle: {
+                  opacity: 0.8,
+                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                    {
+                      offset: 0,
+                      color: 'rgb(227,205,116)'
+                    },
+                    {
+                      offset: 1,
+                      color: 'rgb(234,193,61)'
+                    }
+                  ])
+                },
+                emphasis: {
+                  focus: 'series'
+                },
+                data: statisticsData.value?.data_trend.transaction_counts || []
+              },
+              {
+                name: '交易人数',
+                type: 'line',
+                stack: 'Total',
+                smooth: true,
+                lineStyle: {
+                  width: 0
+                },
+                showSymbol: false,
+                areaStyle: {
+                  opacity: 0.8,
+                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                    {
+                      offset: 0,
+                      color: 'rgb(93,117,236)'
+                    },
+                    {
+                      offset: 1,
+                      color: 'rgb(17,91,227)'
+                    }
+                  ])
+                },
+                emphasis: {
+                  focus: 'series'
+                },
+                data: statisticsData.value?.data_trend.customer_counts || []
+              }
+            ]
+          }
+          chart.setOption(option)
+        }
+      
+        // 首次调用更新函数
+        updateOption()
+      
+        // 监听统计数据变化,更新图表
+        watch(() => statisticsData.value?.data_trend, updateOption, { deep: true })
+      
+        return chart
+    }
+
+    // 返回hook中的响应式数据和方法
+    return {
+        statisticsData,
+        yearMonth,
+        datePickerShow,
+        dateRef,
+        confirm,
+        initChart,
+        initTrendChart
+    }
+}

+ 164 - 0
src/hooks/user-info/useUserInfoHook.tsx

@@ -0,0 +1,164 @@
+import {reactive, ref} from "vue";
+import {getMemberInfo, updateMemberInfo} from "@/api/member";
+import Taro, {useDidShow, useLoad, useUnload} from "@tarojs/taro";
+import {useMemberStoreHook} from "@/store/memberStore";
+import {uploadFile} from "@/api/file";
+import dayjs from "dayjs";
+import {getWechatPhoneNumber} from "@/api/passport";
+import {type StoreUser} from "@/types";
+import { useUserStoreHook } from "@/store/user";
+
+export function useUserInfoHook() {
+    // 从会员存储中获取并初始化用户信息
+    const userInfo = reactive<StoreUser>({
+        ...useMemberStoreHook().member
+    });
+
+    // 性别相关配置
+    const genderPopupVisible = ref(false);  // 控制性别选择弹窗显示状态
+    const genderColumns = ref([
+        {text: '男', value: 1},
+        {text: '女', value: 0}
+    ]);
+    const genderVal = ref<number[]>([userInfo.sex as number])  // 当前选中的性别值
+
+    // 性别选择确认
+    const genderConfirm = ({selectedValue}) => {
+        console.log(selectedValue)
+        userInfo.sex = selectedValue[0]
+        useMemberStoreHook().setMember({...userInfo})
+        genderPopupVisible.value = false;
+    }
+
+    // 格式化显示性别
+    const genderFormatter = (value) => {
+        return value == 1 ? '男' : '女'
+    }
+
+    // 生日选择弹窗
+    const birthdayPopupVisible = ref(false);
+    // 最小日期
+    const minDay = ref(new Date(1940, 0, 1));
+    // 最大日期
+    const maxDay = ref(new Date());
+
+    const birthdayVal = ref(dayjs.unix(userInfo.birthday).toDate())
+
+    // 生日选择确认
+    const birthdayConfirm = ({selectedValue, selectedOptions}) => {
+        console.log('selectedValue', selectedValue, selectedOptions)
+        userInfo.birthday = dayjs(new Date(selectedValue.join('-'))).unix()
+        useMemberStoreHook().setMember({...userInfo})
+        birthdayPopupVisible.value = false;
+    }
+
+    const birthdayFormatter = (value) => {
+        return dayjs.unix(value).format('YYYY-MM-DD')
+    }
+
+    // 头像临时路径
+    const imageUrl = ref<string|null>('');  // 存储头像的临时或永久路径
+    // 选择头像回调
+    const onChooseAvatar = (e) => {
+        console.log('e', e)
+        imageUrl.value = e.detail.avatarUrl       
+        useMemberStoreHook().setMember({...userInfo, face: e.detail.avatarUrl})
+    }
+
+    // 保存用户信息
+    const saveUserInfo = async () => {
+        if (imageUrl.value?.includes('http://tmp/', 0)||imageUrl.value?.includes('wxfile://', 0)) {
+            await uploadFile({filePath: imageUrl.value, scene: 'shop'}).then(res => {
+                console.log('uploadFile', res)
+                userInfo.face = JSON.parse(res.data).url
+            })
+        }
+        updateMemberInfo(userInfo).then((res) => {
+            Taro.showToast({
+                title: '更新成功',
+                icon: 'success',
+                duration: 2000
+            })
+            setTimeout(() => {
+                   console.log('res',res)
+                    useMemberStoreHook().setMember(res.data)                    
+                    Object.assign(userInfo, {...res.data})
+                    imageUrl.value = res.data.face
+                    const {face,nickname,sex,birthday,mobile} = res.data
+                    console.log('userInfo',{face,nickname,sex,birthday,mobile})
+                    useUserStoreHook().setUserInfo({face,nickname,sex,birthday,mobile})
+                
+            }, 2000)
+        }).catch(() => {
+           
+        })
+
+    }
+   // 获取手机号码回调
+    const getPhoneNumber = (e) => {
+        const {errno, code} = e.detail
+        if (errno) {
+            Taro.showToast({
+                title: '获取手机号码失败',
+                icon: 'none',
+                duration: 2000
+            })
+        } else {
+            getWechatPhoneNumber({code: code, mini_program_type: 'miniprogramo2oseller'}).then(res => {
+                const {phone_info: {purePhoneNumber}} = res.data
+                userInfo.mobile = purePhoneNumber
+                useMemberStoreHook().setMember({...userInfo, mobile: purePhoneNumber})
+                console.log('phoneNumber', res)
+            }).catch(() => {
+
+            })
+            console.log('getPhoneNumber', code)
+        }
+    }
+
+    // 页面加载完成
+    useDidShow(() => {
+        if (!useMemberStoreHook().member) {
+            getMemberInfo().then(res => {
+                useMemberStoreHook().setMember(res.data)
+                Object.assign(userInfo, {...res.data})
+                imageUrl.value = res.data.face
+                console.log('userInfo', res)
+            })
+        } else {
+            imageUrl.value = useMemberStoreHook().member.face as string
+            Object.assign(userInfo, {...useMemberStoreHook().member})
+        }
+    })
+
+    // 监听页面卸载
+    useUnload(() => {
+        // 前一个页面
+        const pages = Taro.getCurrentPages()
+        const prevPage = pages[pages.length - 2]
+        console.log('prevPage', prevPage)
+        if(prevPage.route==='pages/login/index'){ // 如果前一个页面是登录页面,则切换到我的页面
+            Taro.switchTab({url: '/pages/my/index'})
+        }
+    })
+
+
+    return {
+        userInfo,
+        genderPopupVisible,
+        birthdayPopupVisible,
+        genderColumns,
+        imageUrl,
+        minDay,
+        maxDay,
+        birthdayVal,
+        genderVal,
+        genderConfirm,
+        birthdayConfirm,
+        genderFormatter,
+        birthdayFormatter,
+        onChooseAvatar,
+        saveUserInfo,
+        getPhoneNumber,
+    }
+}

+ 129 - 0
src/hooks/withdraw/useWithdrawApplyHook.ts

@@ -0,0 +1,129 @@
+//申请提现Hook
+import {ref} from "vue";
+import {CommonPageParam, RebateAccountDO, RebateWithdrawApplyDO} from "@/types";
+import {useShopStoreHook} from "@/store/shopStore";
+import {useUserStoreHook} from "@/store/user";
+import {getBillAccountList} from "@/api/billAccount";
+import {addWithdraw} from "@/api/withdraw";
+import Taro from "@tarojs/taro";
+
+export default function useWithdrawApplyHook() {
+    //提现账户数组
+    const bankAccountOptions = ref<RebateAccountDO[]>([]);
+
+
+
+    //提现账户分页查询参数
+    const commonPageParam = ref<CommonPageParam>({
+        page_no: 1,
+        page_size: 100,
+        fixedCondition: `shop_id=${useShopStoreHook().currentShop.shop_id} and  member_id=${useUserStoreHook().user.uid}`
+    })
+
+    //提现申请表单
+    const withdrawApplyForm = ref<RebateWithdrawApplyDO>({
+        apply_money: 0,
+        apply_remark: '',
+        shop_id: useShopStoreHook().currentShop.shop_id as number,
+        shop_name: useShopStoreHook().currentShop.shop_name||'',
+        apply_name: useUserStoreHook().user.username,
+        apply_member_id: useUserStoreHook().user.uid,
+        bank_name: '',
+        bank_account: '',
+        account_name: '',
+        current_total_rebate_amount: 0,
+        transfer_remark: '',
+        payment_plugin_id: '',
+        status_text: ''
+    })
+    //表单引用
+    const formRef = ref<any>();
+
+    //银行账户文本
+    const bankAccountText = ref('');
+
+    //银行账户选择器显示
+    const bankAccountPickerShow = ref(false);
+
+    //选中的提现账户
+    const bankAccount = ref<RebateAccountDO[]>();
+
+    //确认选择提现账户
+    const bankAccountConfirm = ({selectedOptions}) => {
+        console.log('item', selectedOptions)
+        bankAccount.value = selectedOptions;
+        bankAccountText.value = `${selectedOptions[0].bank_name} ${selectedOptions[0].bank_account}`;
+        bankAccountPickerShow.value = false;
+        withdrawApplyForm.value.rebate_account_id = selectedOptions[0].id;
+        withdrawApplyForm.value.bank_name = selectedOptions[0].bank_name;
+        withdrawApplyForm.value.bank_account = selectedOptions[0].bank_account;
+        withdrawApplyForm.value.account_name = selectedOptions[0].account_name;
+        withdrawApplyForm.value.payment_plugin_id = selectedOptions[0].payment_plugin_id;
+        customBlurValidate('rebate_account_id');
+    }
+
+    //自定义失焦验证
+    const customBlurValidate = (key: string) => {
+        formRef.value?.validate(key);
+    }
+
+    //保存提现申请
+    const saveWithdrawApply = async () => {
+        const {valid:val1, errors} = await formRef.value?.validate()
+        if(val1){
+            addWithdraw(withdrawApplyForm.value).then(res => {
+                Object.assign(withdrawApplyForm.value, res.data)
+                Taro.showToast({
+                    title: '申请成功',
+                    icon: 'success',
+                    duration: 2000
+                })
+
+                console.log(res)
+            }).catch(err => {
+                console.log(err)
+            })
+            //提交提现申请
+            console.log(withdrawApplyForm.value)
+        } else {
+                console.log(errors)
+            }
+
+        // formRef.value.validate(async (valid: boolean) => {
+        //     if (valid) {
+        //         //提交提现申请
+        //         console.log(withdrawApplyForm.value)
+        //     }
+        // })
+    }
+
+    //分页查询提现账户列表
+    const loadAccountList = () => {
+        getBillAccountList(commonPageParam.value).then(res => {
+            bankAccountOptions.value = res.data.data;
+            bankAccountOptions.value.forEach(item => {
+                item.bank_show_text = `${item.bank_name} ${item.bank_account}`;
+            })
+            }
+        ).catch(err => {
+            console.log(err)
+        })
+    }
+    loadAccountList();
+
+
+
+
+
+    return {
+        withdrawApplyForm,
+        formRef,
+        bankAccountText,
+        bankAccountPickerShow,
+        bankAccountOptions,
+        bankAccount,
+        bankAccountConfirm,
+        customBlurValidate,
+        saveWithdrawApply
+    }
+}

+ 136 - 0
src/hooks/withdraw/useWithdrawHook.ts

@@ -0,0 +1,136 @@
+import {CommonPageParam, RebateShopVO, RebateWithdrawApplyDO} from "@/types";
+import {computed, ref, watch} from "vue";
+import {getAccountBalance, getWithdrawList} from "@/api/withdraw";
+import {usePullDownRefresh, useReachBottom} from "@tarojs/taro";
+import {useShopStoreHook} from "@/store/shopStore";
+import Taro from "@tarojs/taro";
+
+export default function useWithdrawhook(){
+
+    //当前登陆店铺
+    const currentShop = computed(() => useShopStoreHook().currentShop);
+
+    //提现账户记录数组
+    const withdrawList = ref<RebateWithdrawApplyDO[]>([]);
+
+    //店铺账户余额
+    const shopRebate = ref<RebateShopVO>({
+        total_rebate_amount: 0,
+        withdraw_amount: 0,
+        lock_amount: 0
+    });
+
+    //分页查询参数
+    const commonPageParam = ref<CommonPageParam>({
+        page_no: 1,
+        page_size: 10,
+        fixedCondition: `shop_id=${useShopStoreHook().currentShop.shop_id}`
+    })
+
+    //是否加载完成
+    const loadFinished = ref(false);
+
+    //分页查询提现列表
+    const loadWithdrawList = () => {
+        getWithdrawList(commonPageParam.value).then(res => {
+            if (res.data.data.length < commonPageParam.value.page_size) {
+                loadFinished.value = true;
+            }
+            if (commonPageParam.value.page_no === 1) {
+                withdrawList.value = res.data.data;
+            } else {
+                withdrawList.value = [...withdrawList.value, ...res.data.data];
+            }
+            Taro.hideNavigationBarLoading();
+            Taro.stopPullDownRefresh();
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+
+    //触底加载更多
+    useReachBottom(() => {
+        if (!loadFinished.value) {
+            commonPageParam.value.page_no++;
+            loadWithdrawList();
+        }
+    })
+    //下拉刷新
+   usePullDownRefresh( () => {
+        commonPageParam.value.page_no = 1;
+        loadFinished.value = false;
+        Taro.showNavigationBarLoading();
+        loadShopRebate();
+        loadWithdrawList();
+    })
+
+   //加载账户余额
+    const loadShopRebate = () => {
+        getAccountBalance(useShopStoreHook().currentShop.shop_id as number).then(res => {
+            shopRebate.value = res.data;
+        }).catch(err => {
+            console.log(err)
+        })
+    }
+    if(useShopStoreHook().currentShop.shop_id){
+    //加载提现列表
+    loadWithdrawList();
+    //加载账户余额
+    loadShopRebate();
+    }
+
+    watch(()=>useShopStoreHook().currentShop?.shop_id,()=>{
+        if(useShopStoreHook().currentShop?.shop_id){          
+            loadShopRebate();
+        }
+    })
+
+    //激活的tab
+    const tabValue=ref('ALL')
+    //tab切换项目
+    const tabList = ref([
+        {
+            title: '全部记录',
+            paneKey: 'ALL'
+        },
+        {
+            title: '申请中',
+            paneKey: 'APPLY'
+        },
+        {
+            title: '审核通过',
+            paneKey: 'VIA_AUDITING'
+        },
+        {
+            title: '审核不通过',
+            paneKey: 'FAIL_AUDITING'
+        },
+        {
+            title: '已转账',
+            paneKey: 'TRANSFER_ACCOUNTS'
+        }
+    ])
+
+    //tab切换
+    const tabChange = (value: string) => {
+
+        tabValue.value = value;
+        commonPageParam.value.page_no = 1;
+        if(value==='ALL'){
+        commonPageParam.value.fixedCondition=`shop_id=${useShopStoreHook().currentShop.shop_id}`;
+        }else{
+        commonPageParam.value.fixedCondition = `shop_id=${useShopStoreHook().currentShop.shop_id} and status='${value}'`;
+        }
+        loadFinished.value = false;
+        loadWithdrawList();
+    }
+
+    return{
+        withdrawList,
+        shopRebate,
+        currentShop,
+        tabValue,
+        tabList,
+        tabChange
+    }
+}

+ 34 - 0
src/hooks/workbench/useShopStatisticHook.ts

@@ -0,0 +1,34 @@
+import {Ref, ref, watch} from "vue";
+import {getShopStatistic} from "@/api/shop";
+import {ShopStatistics} from "@/types";
+import { useShopStoreHook } from "@/store/shopStore";
+import { useDidShow } from "@tarojs/taro";
+
+export default function useShopStatisticHook() {
+    // 店铺统计数据响应式引用
+    const shopStatistic:Ref<ShopStatistics|undefined> = ref();
+
+    /**
+     * 加载店铺统计数据
+     */
+    const loadShopStatistic = () => {
+        getShopStatistic().then(res => {
+            shopStatistic.value = res.data;
+        });
+    };
+    // 监听页面显示,加载店铺统计数据
+    useDidShow(()=>{
+        loadShopStatistic();
+    })
+
+    // 监听当前店铺ID变化,当ID存在时重新加载统计数据
+    watch(()=>useShopStoreHook().currentShop?.shop_id,()=>{
+        if(useShopStoreHook().currentShop?.shop_id){
+            loadShopStatistic();
+        }
+    })
+
+    return {
+        shopStatistic
+    };
+}

+ 65 - 0
src/hooks/workbench/useWorkbenchHook.tsx

@@ -0,0 +1,65 @@
+import {useUserStoreHook} from "@/store/user";
+import {ref, watch} from "vue";
+import {useShopStoreHook} from "@/store/shopStore";
+import dayjs from "dayjs";
+import customParseFormat from 'dayjs/plugin/customParseFormat'
+import {updateShopStatus} from "@/api/shop";
+import Taro, { useDidShow } from "@tarojs/taro";
+import { Shop } from "@/types";
+dayjs.extend(customParseFormat)
+export function useWorkbenchHook() {
+    // 获取当前商店信息的计算属性
+    const currentShopInfo =ref<Shop>(useShopStoreHook().currentShop)
+    
+    // 判断是否为卖家的计算属性
+    const bool_seller = ref<boolean>(useUserStoreHook().user.bool_seller)
+    
+    /**
+     * 更改商店状态的方法
+     * @param manual - 是否手动触发更改
+     */
+    const changeShopState =(manual)=>{
+        if(manual){
+            // 解构获取商店相关信息
+            const {shop_id,shop_state,business_date,start_time,end_time} = currentShopInfo.value
+            // 调用API更新商店状态
+            updateShopStatus({shopId:shop_id,shopState:shop_state,businessDate:business_date,startTime:start_time,endTime:end_time}).then(()=>{
+                // 更新成功后显示提示
+                Taro.showToast({
+                    title: '修改成功',
+                    icon: 'success',
+                    duration: 2000
+                });
+                // 重新获取商店列表
+                useShopStoreHook().getShops();
+            })}
+    }
+
+    // // 页面显示时触发的钩子
+    // useDidShow(()=>{
+    //     console.log('useDidShow',useShopStoreHook().currentShop.shop_id)
+    // })
+    watch(()=>useShopStoreHook().currentShop?.shop_id,(newVal)=>{
+        console.log('useWorkbenchHook',newVal)
+        currentShopInfo.value = useShopStoreHook().currentShop
+        bool_seller.value = useUserStoreHook().user.bool_seller
+    },{deep:true})
+
+    const subscribeMessage=()=>{
+       Taro.requestSubscribeMessage({
+           tmplIds: ['Y2LCDRXld1ccrdmPavW4gBi1RhI8rjVyeOUencmPMpw'],
+           success: () => {
+               console.log('订阅成功');
+           },
+           entityIds: []
+       })
+    }
+
+    // 返回需要暴露的属性和方法
+    return {
+        bool_seller,
+        currentShopInfo,
+        changeShopState,
+        subscribeMessage
+    };
+}

+ 34 - 0
src/hooks/workbench/useWorkbenchSwitchShopHook.ts

@@ -0,0 +1,34 @@
+import {useShopStoreHook} from "@/store/shopStore";
+import {computed, ref} from "vue";
+import {switchShop} from "@/api/passport";
+import {useUserStoreHook} from "@/store/user";
+import Taro from "@tarojs/taro";
+
+export default function useWorkbenchSwitchShopHook() {
+    //店铺列表
+    const shops = computed(() => useShopStoreHook().shopList);
+    //切换店铺弹出框显示状态
+    const switchShopVisable = ref(false)
+    //切换店铺
+    const changeShop = (shop) => {
+        switchShop(shop.shop_id).then(async res=>{
+            Taro.showToast({
+                title: '切换成功',
+                icon: 'success',
+                duration: 2000,
+                success:()=>{
+                    setTimeout(()=>{switchShopVisable.value = false},2000)
+                }
+            });
+
+            useUserStoreHook().setUserInfo({...res.data,bool_seller:true})
+            await useShopStoreHook().getShops()
+        })
+    }
+
+    return {
+        shops,
+        switchShopVisable,
+        changeShop
+    }
+}

+ 6 - 0
src/http/baseUrl.ts

@@ -0,0 +1,6 @@
+export const sellerBaseUrl=process.env.TARO_APP_SELLER_URL
+export const baseBaseUrl=process.env.TARO_APP_BASE_URL
+export const orgBaseUrl=process.env.TARO_APP_ORG_URL
+export const buyerBaseUrl=process.env.TARO_APP_BUYER_URL
+
+

+ 201 - 0
src/http/request.ts

@@ -0,0 +1,201 @@
+import Taro from '@tarojs/taro';
+import { useUserStoreHook } from "../store/user";
+import {useUuidStoreHook} from "../store/uuidStore";
+import {nanoid} from "nanoid/non-secure";
+import {sellerBaseUrl} from "./baseUrl";
+import {md5} from "js-md5";
+import dayjs from "dayjs";
+import advancedFormat from 'dayjs/plugin/advancedFormat'
+import {useMemberStoreHook} from "../store/memberStore";
+import {navigateTo, redirectTo} from "../utils/commonJs";
+import qs from 'qs'
+import {RefreshToken} from "@/types";
+import { useShopCategoryStoreHook } from '@/store/shopCategoryStore';
+import { useShopStoreHook } from '@/store/shopStore';
+dayjs.extend(advancedFormat);
+
+
+// 默认请求头
+const defaultHeader = {
+    'Content-Type': 'application/json;',
+    'Accept': "application/json, text/plain, */*",
+    "X-Requested-With": "XMLHttpRequest",
+};
+// 刷新 token 的方法
+const refreshToken = async () => {
+    try {
+        const { refresh_token} = useUserStoreHook().user;
+        const response = await request<RefreshToken>({
+            url: '/passport/mini-program/token', // 示例,根据实际情况修改
+            method: 'POST',
+            header:{
+                'Content-Type': 'application/x-www-form-urlencoded',
+            },
+            data: { refresh_token: refresh_token },
+            showLoading: true, // 是否显示加载提示
+        });
+        // 更新本地存储的 token 信息
+        useUserStoreHook().setToken(response.data)
+        return response.data.access_token;
+    } catch (error) {
+        return Promise.reject(error);
+    }
+};
+
+// 自定义拦截器函数
+const customInterceptor = async (chain) => {
+    console.log('chain',chain)
+    const {  url, data } = chain.requestParams;
+    const {uid, access_token,expires} = useUserStoreHook().user;
+     defaultHeader['uuid'] = useUuidStoreHook().uuid
+    // 检查 token 是否过期
+    const currentTimestamp = Math.floor(Date.now() / 1000); // 当前时间戳,单位秒
+    /** 请求白名单,放置一些不需要`token`的接口(通过设置请求白名单,防止`token`过期后再请求造成的死循环问题) */
+    const whiteList = ["/passport/mini-program/token", "/login"];
+    const {method} = chain.requestParams
+    if (!whiteList.some(p=>url.endsWith(p))&&expires && currentTimestamp >= expires) {
+        try {
+            // 刷新 token
+            const newToken = await refreshToken();
+            // 更新请求头中的 token
+            if(process.env.NODE_ENV === 'production'){ //开发环境
+                defaultHeader['Authorization'] = newToken
+            }else{ //生产环境
+                const nonce = nanoid(6)
+                const timestamp = dayjs().format("X")
+                console.log('sign',`${uid}${nonce}${timestamp}${newToken}`)
+                const sign = md5(`${uid}${nonce}${timestamp}${newToken}`)
+
+                const _params = { uid, nonce, timestamp, sign }
+                let _data = data || {}
+                if(method.toUpperCase()==='GET') {
+                    _data = {..._data, ..._params}
+                }else{
+                    var queryStr = qs.stringify(_params, {
+                        encoder: (value) => value === null ? '' : value
+                    });
+                    chain.requestParams.url = chain.requestParams.url + '?' + queryStr
+                }
+                chain.requestParams.data = _data
+            }
+        } catch (error) {
+            // 处理刷新 token 失败的情况,登陆已失效,退出登陆
+            useUserStoreHook().clearUser();
+            useMemberStoreHook().clearMember();
+            navigateTo('/pages/login/index')
+            console.error('Failed to refresh token:', error);
+            return Promise.reject(error);
+        }
+    } else {
+
+        if(process.env.NODE_ENV === 'product'){ //开发环境
+            defaultHeader['Authorization'] = access_token
+        }else{ //生产环境
+            const nonce = nanoid(6)
+            const timestamp = dayjs().format("X")
+            console.log('sign',`${uid}${nonce}${timestamp}${access_token}`)
+            const sign =  md5(`${uid}${nonce}${timestamp}${access_token}`)
+            console.log('sign',`${uid}${nonce}${timestamp}${access_token}`,sign)
+            const _params = { uid, nonce, timestamp, sign }
+            let _data = data || {}
+            if(method.toUpperCase()==='GET') {
+                _data = {..._data, ..._params}
+            }else{
+                var queryStr = qs.stringify(_params, {
+                    encoder: (value) => value === null ? '' : value
+                });
+                chain.requestParams.url = chain.requestParams.url + '?' + queryStr
+            }
+            chain.requestParams.data = _data
+        }
+    }
+
+    // 设置默认请求头
+    chain.requestParams.header={...defaultHeader,...chain.requestParams.header}
+
+    if((method.toUpperCase()==='POST'||method.toUpperCase()==='PUT')&&chain.requestParams.header['Content-Type']==='application/x-www-form-urlencoded'){
+        chain.requestParams.data = qs.stringify(chain.requestParams.data, {
+            encoder: (value) => value === null ? '' : value
+        });
+    }
+
+    // 显示加载提示
+    if (chain.requestParams.showLoading) {
+        Taro.showLoading({
+            title: '加载中...',
+            mask: true,
+        });
+    }
+
+    // 继续请求
+    return chain.proceed(chain.requestParams)
+        .then((res) => {
+            // 隐藏加载提示
+            if (chain.requestParams.showLoading) {
+                Taro.hideLoading();
+            }
+
+            if(res.statusCode === 200) {
+                console.log('chain res',res)
+                return Promise.resolve(res);
+            }else if(res.statusCode === 401) {
+                // 处理 token 过期
+                console.error('Token expired');
+                return Promise.reject('Token expired');
+            }else if (res.statusCode === 403) {
+                // 处理没有权限,退出登陆
+                useUserStoreHook().clearUser();
+                useMemberStoreHook().clearMember();
+                useShopCategoryStoreHook().clearShopCategoryList();
+                useShopStoreHook().clearShop();
+                
+                Taro.showToast({
+                    title: '登陆已失效,请重新登陆',
+                    icon: 'none',
+                    duration: 2000
+                })
+                setTimeout(() => {
+                    redirectTo('/pages/login/index')
+                }, 2000);
+                return Promise.reject('No permission');
+            }else if (res.statusCode === 500||res.statusCode===400) {
+                // 处理服务器错误
+                Taro.showToast({
+                    title: res.data.message,
+                    icon: 'none',
+                    duration: 2000
+                })
+                return Promise.reject(res);
+            }
+
+        })
+        .catch((err) => {
+            // 隐藏加载提示
+            if (chain.requestParams.showLoading) {
+                Taro.hideLoading();
+            }
+
+            return Promise.reject(err);
+        });
+};
+
+// 添加请求拦截器
+Taro.addInterceptor(customInterceptor);
+
+// 封装统一请求函数
+const request = <T>(options) => {
+    const { url, method = 'GET', data = {}, showLoading = true, baseURL=sellerBaseUrl,header={} } = options;
+
+    const requestOptions = {
+        url: baseURL + url,
+        header,
+        method,
+        data,
+        showLoading,
+    };
+
+
+    return Taro.request<T>(requestOptions);
+};
+
+export default request;

BIN
src/images/account-login.png


BIN
src/images/account.png


BIN
src/images/after-sale.png


BIN
src/images/bill.png


BIN
src/images/category.png


BIN
src/images/clerk.png


BIN
src/images/comment.png


BIN
src/images/cooperation.png


BIN
src/images/dashboard_bg.png


BIN
src/images/device.png


BIN
src/images/empty-status.png


BIN
src/images/fund.png


BIN
src/images/glass-add.png


BIN
src/images/glass-document.png


BIN
src/images/glass-lock.png


BIN
src/images/glass-paper.png


BIN
src/images/glass-profile.png


BIN
src/images/glass-setting.png


BIN
src/images/glass-shield.png


+ 0 - 0
src/images/glass-shops.png


Некоторые файлы не были показаны из-за большого количества измененных файлов