فهرست منبع

初始化项目

朱永杰 2 هفته پیش
کامیت
5da59ab674
100فایلهای تغییر یافته به همراه19814 افزوده شده و 0 حذف شده
  1. 4 0
      .env.development
  2. 4 0
      .env.production
  3. 4 0
      .env.testing
  4. 9 0
      .gitignore
  5. 35 0
      README.md
  6. 143 0
      VERSION.md
  7. 14 0
      index.html
  8. 41 0
      package.json
  9. BIN
      public/favicon.ico
  10. 30 0
      src/App.vue
  11. BIN
      src/assets/AYN.png
  12. BIN
      src/assets/AYNlogo2.png
  13. 87 0
      src/assets/iconfont/iconfont.css
  14. 1 0
      src/assets/iconfont/iconfont.js
  15. 135 0
      src/assets/iconfont/iconfont.json
  16. BIN
      src/assets/iconfont/iconfont.ttf
  17. BIN
      src/assets/iconfont/iconfont.woff
  18. BIN
      src/assets/iconfont/iconfont.woff2
  19. BIN
      src/assets/images/401.gif
  20. BIN
      src/assets/images/404.png
  21. BIN
      src/assets/images/404_cloud.png
  22. BIN
      src/assets/images/default.png
  23. BIN
      src/assets/images/logo.jpg
  24. BIN
      src/assets/images/logo2.jpg
  25. BIN
      src/assets/images/tasklogo.png
  26. BIN
      src/assets/logo.png
  27. BIN
      src/assets/noclassify.png
  28. BIN
      src/assets/nodata.png
  29. 91 0
      src/assets/style/common.scss
  30. 37 0
      src/assets/style/transition.scss
  31. 16 0
      src/components/imgPreview/index.js
  32. 26 0
      src/components/imgPreview/index.vue
  33. 12 0
      src/components/menu/index.vue
  34. 102 0
      src/components/table/index.vue
  35. 192 0
      src/components/wangEditor/BasicEditor.vue
  36. 5 0
      src/config/index.js
  37. 61 0
      src/layout/Header/Breadcrumb.vue
  38. 31 0
      src/layout/Header/functionList/fullscreen.vue
  39. 62 0
      src/layout/Header/functionList/sizeChange.vue
  40. 173 0
      src/layout/Header/functionList/theme.vue
  41. 77 0
      src/layout/Header/functionList/theme/theme-color.vue
  42. 130 0
      src/layout/Header/functionList/theme/theme-icon.vue
  43. 152 0
      src/layout/Header/index.vue
  44. 47 0
      src/layout/Logo/index.vue
  45. 41 0
      src/layout/Menu/Link.vue
  46. 100 0
      src/layout/Menu/MenuItem.vue
  47. 120 0
      src/layout/Menu/index.vue
  48. 265 0
      src/layout/Tabs/index.vue
  49. 100 0
      src/layout/Tabs/item.vue
  50. 9 0
      src/layout/Tabs/tabsHook.js
  51. 134 0
      src/layout/index.vue
  52. 24 0
      src/main.js
  53. 15 0
      src/request/api.js
  54. 37 0
      src/request/apiConfig.js
  55. 132 0
      src/request/modules/apiContent.js
  56. 92 0
      src/request/modules/apiCow.js
  57. 11 0
      src/request/modules/apiEcontract.js
  58. 33 0
      src/request/modules/apiItem.js
  59. 19 0
      src/request/modules/apiOrder.js
  60. 21 0
      src/request/modules/apiUser.js
  61. 46 0
      src/router/createNode.js
  62. 61 0
      src/router/index.js
  63. 32 0
      src/router/modules/dashboard.js
  64. 345 0
      src/router/modules/pages.js
  65. 43 0
      src/router/modules/system.js
  66. 116 0
      src/router/reload.vue
  67. 29 0
      src/store/index.js
  68. 44 0
      src/store/modules/app.js
  69. 41 0
      src/store/modules/keepAlive.js
  70. 79 0
      src/store/modules/user.js
  71. 9 0
      src/store/mutation-types.js
  72. 39 0
      src/store/plugins/persistent.js
  73. 99 0
      src/theme/index.js
  74. 51 0
      src/theme/index.scss
  75. 25 0
      src/theme/modules/dark.scss
  76. 134 0
      src/utils/system/filters.js
  77. 12 0
      src/utils/system/nprogress.js
  78. 98 0
      src/utils/system/request.js
  79. 39 0
      src/utils/system/time.js
  80. 6 0
      src/utils/system/title.js
  81. 32 0
      src/utils/system/uploadCompress.js
  82. 143 0
      src/views/IntendedCustomers/IntendedCustomers.vue
  83. 420 0
      src/views/beefSaleManage/addNewbeef.vue
  84. 11535 0
      src/views/beefSaleManage/assets/area.json
  85. 260 0
      src/views/beefSaleManage/beefInStore.vue
  86. 214 0
      src/views/beefSaleManage/beefOnsaleList.vue
  87. 460 0
      src/views/beefSaleManage/editBeef.vue
  88. 304 0
      src/views/cattleTrdeValue/index.vue
  89. 887 0
      src/views/cattleTrdeValue/recondsInfo.vue
  90. 132 0
      src/views/cattleTrdeValue/referenceInfo.vue
  91. 315 0
      src/views/classifyManage/classifyManageIndex.vue
  92. 157 0
      src/views/contentManage/contentList.vue
  93. 211 0
      src/views/contentManage/contentTypeList.vue
  94. 249 0
      src/views/contentManage/newContent.vue
  95. 95 0
      src/views/dashboard/components/cardBox.vue
  96. 87 0
      src/views/dashboard/components/desCard.vue
  97. 1 0
      src/views/dashboard/components/static/china.json
  98. 1 0
      src/views/dashboard/components/static/shq.json
  99. 89 0
      src/views/dashboard/index.vue
  100. 0 0
      src/views/intentManage/buyIntent.vue

+ 4 - 0
.env.development

@@ -0,0 +1,4 @@
+ENV = 'development'
+
+VITE_BASE_URL = '/dev-api'
+outputDir = 'building for developing please wait...'

+ 4 - 0
.env.production

@@ -0,0 +1,4 @@
+NODE_ENV = 'production'
+
+VITE_BASE_URL = '/pro-api'
+outputDir = 'building for production please wait...'

+ 4 - 0
.env.testing

@@ -0,0 +1,4 @@
+NODE_ENV = 'testing'
+
+VITE_BASE_URL = '/test-api'
+outputDir = 'building for testing please wait...'

+ 9 - 0
.gitignore

@@ -0,0 +1,9 @@
+cattletrading*
+node_modules
+.DS_Store
+purchasePriceTrend
+dist-ssr
+*.local
+yarn.lock
+yarn-error.log
+.vscode

+ 35 - 0
README.md

@@ -0,0 +1,35 @@
+
+#### 主要技术栈
+
+- MVVM框架:vue v3
+- 工程化管理:vite v2
+- UI框架:element-plus
+- 路由管理:vue-router v4
+- 状态管理:vuex v4
+- 数据请求:axios
+- 实用工具库:@vueuse/core
+
+
+2. 安装依赖,国内推荐使用cnpm或tyarn,国外推荐使用npm或yarn
+
+   ```
+   npm install
+   ```
+
+   
+
+3. 运行
+
+   ```
+   npm run dev 或 npm run start
+   ```
+
+   
+
+4. 打包
+
+   ```
+   npm run build
+   ```
+   
+

+ 143 - 0
VERSION.md

@@ -0,0 +1,143 @@
+# 版本更新日志
+
+## 0.41版本
+1. 【修改】菜单过长时,滚动条问题
+2. 【新增】主题配置功能,核心编辑代码编写
+
+## 0.40版本
+1. 【优化】路由keep-alive状态保存与删除
+2. 【新增】当前页刷新,缓存页刷新机制
+3. 【新增】关闭标签栏时的缓存页对应状态变化
+
+## 0.32版本
+1. 【优化】左侧菜单栏复杂情况下的示例应用
+2. 【新增】分类联动表格的实现
+3. 【新增】树联动表格的实现
+4. 【BUG】表格+开发BUG修复
+
+## 0.31版本
+1. 【新增】指令集模块完成
+2. 【新增】echarts图表模块完成
+
+## 0.24版本
+```
+1. 【新增】弹窗截图功能
+```
+
+## 0.23版本
+```
+1. 【新增】代码编辑器
+2. 【新增】JSON编辑器
+3. 【新增】可拖拽面板
+4. 【新增】地图组件
+```
+
+## 0.22版本
+```
+1. 【新增】MD编辑器
+```
+
+## 0.21版本
+```
+1. 【统计】统计大部分需要实现的功能页面,并展示于左侧菜单中
+2. 【新增】新增大部分需要实现的路由,并确保,大部分能于7月1日之前完全实现
+```
+
+## 0.11版本
+```
+1. 【新增】新增组件栏目,并补充按钮组进入
+2. 【优化】多级菜单跳转demo
+3. 【优化】侧边栏功能优化
+```
+
+## 0.10版本
+```
+1. 【新增】实现多级菜单Demo
+2. 【优化】当菜单数据量过高时,优化显示
+```
+
+## 0.9版本
+```
+1. 【优化】弹窗组件可拖拽
+2. 【优化】弹窗组件内部暴露逻辑,供外部使用
+```
+
+## 0.8版本
+```
+1. 【优化】axios提示配置
+2. 【优化】公用组件内部逻辑及外部调用方法
+3. 【补充】业务表格模块的数据调用方式
+```
+
+## 0.7版本
+```
+1. 【优化】element-ui国际化配置指南
+2. 【优化】国际化配置本地存储
+```
+
+## 0.6版本
+```
+1. 【新增】按钮尺寸调整功能,针对element-plus的全局尺寸调整
+2. 【新增】首页,图表类展示
+3. 【新增】页面模块,目前包含了业务表格,主要为较为完善的crud写法,正在完善中
+4. 【新增】基于echarts封装的业务chart组件,用户可快速利用此组件生成需要的图表
+```
+
+## 0.5.1版本
+```
+1. 【修复】mock地址,尽量不要使用较短的url,否则,打包上线后,较长的URL容易被冲突掉,示例:登录url和退出登录URL
+```
+
+## 0.5版本
+```
+1. 【优化】本地mock地址使用细节补充及细节优化
+2. 【新增】登录页鉴权
+3. 【新增】退出登录功能
+4. 【优化】本地封装的axios插件优化使用方案
+```
+
+## 0.4版本
+
+```
+1. 【新增】登录页面,及手机自适应处理
+2. 【新增】axios插件,请求处理机制建立
+3. 【优化】全局/@/替换为@/
+4. 【新增】本地mock地址模拟
+```
+
+## 0.3.1版本
+
+```
+1. 【实现】面包屑导航三种关闭功能实现
+```
+
+## 0.3版本
+
+```
+1. 【新增】github跳转链接
+2. 【新增】主题配置功能[页面制作实现]
+3. 【新增】vuex持久化插件,手写版
+```
+
+## 0.2版本
+
+```
+1. 【新增】框架国际化处理
+2. 【BUG】打包上线上路由跳转功能异常,已修复
+```
+
+
+
+## 0.1版本
+
+```
+1. 集成vue-router,vuex,@vueuse/core,element-plus等核心插件
+2. 全局状态管理方案实现
+3. 全局路由管理方案实现
+4. 核心组件库引入处理
+5. 全局layout制作,及自适应处理
+6. 菜单解决方案实现
+7. 面包屑导航实现
+8. 全屏功能实现
+```
+

+ 14 - 0
index.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.ico" />
+    <link rel="stylesheet" href="//at.alicdn.com/t/font_2570680_2fgczr3435f.css">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title></title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 41 - 0
package.json

@@ -0,0 +1,41 @@
+{
+  "name": "vue-admin",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build --mode production",
+    "test": "vue-cli-service build --mode testing",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "@vueuse/core": "^9.1.1",
+    "@wangeditor/editor": "^5.1.15",
+    "@wangeditor/editor-for-vue": "next",
+    "axios": "^0.21.1",
+    "element-plus": "^2.7.3",
+    "image-conversion": "^2.1.1",
+    "lodash": "^4.17.21",
+    "moment": "^2.29.4",
+    "normalize.css": "^8.0.1",
+    "nprogress": "^0.2.0",
+    "qs": "^6.11.0",
+    "throttle-debounce": "^3.0.1",
+    "view-ui-plus": "^1.3.1",
+    "vue": "^3.4.27",
+    "vue-router": "4",
+    "vue3-count-to": "^1.1.2",
+    "vuex": "^4.0.0"
+  },
+  "devDependencies": {
+    "@types/node": "^15.0.3",
+    "@vue/cli-plugin-babel": "~4.5.0",
+    "@vue/cli-plugin-router": "~4.5.0",
+    "@vue/cli-plugin-vuex": "~4.5.0",
+    "@vue/cli-service": "~4.5.0",
+    "@vue/compiler-sfc": "^3.0.5",
+    "sass": "^1.32.12",
+    "sass-loader": "^8.0.2",
+    "vue-tsc": "^0.0.24"
+  }
+}

BIN
public/favicon.ico


+ 30 - 0
src/App.vue

@@ -0,0 +1,30 @@
+<template>
+  <el-config-provider :locale="locale">
+    <router-view></router-view>
+  </el-config-provider>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import locale from 'element-plus/lib/locale/lang/zh-cn'
+export default defineComponent({
+  name: 'App',
+  setup() {
+    return {
+      locale
+    }
+  }
+})
+</script>
+
+<style>
+#app {
+  font-family: Avenir, Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  /* text-align: center; */
+  color: #2c3e50;
+  width: 100%;
+  height: 100vh;
+}
+</style>

BIN
src/assets/AYN.png


BIN
src/assets/AYNlogo2.png


+ 87 - 0
src/assets/iconfont/iconfont.css

@@ -0,0 +1,87 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 3856542 */
+  src: url('iconfont.woff2?t=1683171384111') format('woff2'),
+       url('iconfont.woff?t=1683171384111') format('woff'),
+       url('iconfont.ttf?t=1683171384111') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-fabu:before {
+  content: "\e630";
+}
+
+.icon-caigoudingdan:before {
+  content: "\e687";
+}
+
+.icon-_guanggaochuangyi:before {
+  content: "\e606";
+}
+
+.icon-hezuo:before {
+  content: "\e60b";
+}
+
+.icon-dingdandingdanmingxishouzhimingxi:before {
+  content: "\e789";
+}
+
+.icon-yingyong:before {
+  content: "\e600";
+}
+
+.icon-xialazhankai:before {
+  content: "\e6b5";
+}
+
+.icon-xialajiantouxiao:before {
+  content: "\e87e";
+}
+
+.icon-shuaxin:before {
+  content: "\e613";
+}
+
+.icon-quanping1:before {
+  content: "\e62f";
+}
+
+.icon-shezhixitongshezhigongnengshezhishuxing:before {
+  content: "\e795";
+}
+
+.icon--nainiu:before {
+  content: "\e612";
+}
+
+.icon-caidanshouqi:before {
+  content: "\e611";
+}
+
+.icon-caidanzhankai:before {
+  content: "\e615";
+}
+
+.icon-tuichuquanping:before {
+  content: "\e792";
+}
+
+.icon-guanbi1:before {
+  content: "\e723";
+}
+
+.icon-fontsize:before {
+  content: "\e732";
+}
+
+.icon-shezhi:before {
+  content: "\e614";
+}
+

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
src/assets/iconfont/iconfont.js


+ 135 - 0
src/assets/iconfont/iconfont.json

@@ -0,0 +1,135 @@
+{
+  "id": "3856542",
+  "name": "牛只交易项目",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon-",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "978315",
+      "name": "发布",
+      "font_class": "fabu",
+      "unicode": "e630",
+      "unicode_decimal": 58928
+    },
+    {
+      "icon_id": "797502",
+      "name": "采购订单",
+      "font_class": "caigoudingdan",
+      "unicode": "e687",
+      "unicode_decimal": 59015
+    },
+    {
+      "icon_id": "9058113",
+      "name": "7_11广告创意",
+      "font_class": "_guanggaochuangyi",
+      "unicode": "e606",
+      "unicode_decimal": 58886
+    },
+    {
+      "icon_id": "7819601",
+      "name": "招商管理",
+      "font_class": "hezuo",
+      "unicode": "e60b",
+      "unicode_decimal": 58891
+    },
+    {
+      "icon_id": "6129155",
+      "name": "114订单、订单明细、收支明细",
+      "font_class": "dingdandingdanmingxishouzhimingxi",
+      "unicode": "e789",
+      "unicode_decimal": 59273
+    },
+    {
+      "icon_id": "1301",
+      "name": "应用",
+      "font_class": "yingyong",
+      "unicode": "e600",
+      "unicode_decimal": 58880
+    },
+    {
+      "icon_id": "1486990",
+      "name": "下拉展开",
+      "font_class": "xialazhankai",
+      "unicode": "e6b5",
+      "unicode_decimal": 59061
+    },
+    {
+      "icon_id": "2076220",
+      "name": " 下拉箭头小",
+      "font_class": "xialajiantouxiao",
+      "unicode": "e87e",
+      "unicode_decimal": 59518
+    },
+    {
+      "icon_id": "2152145",
+      "name": "刷新",
+      "font_class": "shuaxin",
+      "unicode": "e613",
+      "unicode_decimal": 58899
+    },
+    {
+      "icon_id": "5650911",
+      "name": "全屏",
+      "font_class": "quanping1",
+      "unicode": "e62f",
+      "unicode_decimal": 58927
+    },
+    {
+      "icon_id": "6129178",
+      "name": "138设置、系统设置、功能设置、属性",
+      "font_class": "shezhixitongshezhigongnengshezhishuxing",
+      "unicode": "e795",
+      "unicode_decimal": 59285
+    },
+    {
+      "icon_id": "9251668",
+      "name": "02-奶牛",
+      "font_class": "-nainiu",
+      "unicode": "e612",
+      "unicode_decimal": 58898
+    },
+    {
+      "icon_id": "11556685",
+      "name": "菜单收起",
+      "font_class": "caidanshouqi",
+      "unicode": "e611",
+      "unicode_decimal": 58897
+    },
+    {
+      "icon_id": "11556695",
+      "name": "菜单展开",
+      "font_class": "caidanzhankai",
+      "unicode": "e615",
+      "unicode_decimal": 58901
+    },
+    {
+      "icon_id": "22530095",
+      "name": "退出全屏",
+      "font_class": "tuichuquanping",
+      "unicode": "e792",
+      "unicode_decimal": 59282
+    },
+    {
+      "icon_id": "577309",
+      "name": "关闭1",
+      "font_class": "guanbi1",
+      "unicode": "e723",
+      "unicode_decimal": 59171
+    },
+    {
+      "icon_id": "586897",
+      "name": "font-size",
+      "font_class": "fontsize",
+      "unicode": "e732",
+      "unicode_decimal": 59186
+    },
+    {
+      "icon_id": "1106434",
+      "name": "设置",
+      "font_class": "shezhi",
+      "unicode": "e614",
+      "unicode_decimal": 58900
+    }
+  ]
+}

BIN
src/assets/iconfont/iconfont.ttf


BIN
src/assets/iconfont/iconfont.woff


BIN
src/assets/iconfont/iconfont.woff2


BIN
src/assets/images/401.gif


BIN
src/assets/images/404.png


BIN
src/assets/images/404_cloud.png


BIN
src/assets/images/default.png


BIN
src/assets/images/logo.jpg


BIN
src/assets/images/logo2.jpg


BIN
src/assets/images/tasklogo.png


BIN
src/assets/logo.png


BIN
src/assets/noclassify.png


BIN
src/assets/nodata.png


+ 91 - 0
src/assets/style/common.scss

@@ -0,0 +1,91 @@
+@import "./transition.scss";
+@import "@/theme/index.scss";
+.layout-container {
+  background-color: var(--system-container-main-background);
+  width: calc(100% - 30px);
+  height: calc(100% - 30px);
+  margin: 15px;
+  display: flex;
+  flex-direction: column;
+  overflow-y: auto;
+  &-form {
+    display: flex;
+    justify-content: space-between;
+    padding: 15px 15px 0;
+    &-handle {
+      display: flex;
+      justify-content: flex-start;
+    }
+    &-search {
+      display: flex;
+      justify-content: flex-end;
+      .search-btn {
+        margin-left: 15px;
+      }
+    }
+    .el-form-item {
+      margin-bottom: 0;
+    }
+  }
+  &-table {
+    flex: 1;
+    height: 100%;
+    padding: 15px;
+    overflow: auto;
+  }
+}
+.flex-box {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  height: 100%;
+  padding: 15px;
+  box-sizing: border-box;
+}
+.flex {
+  display: flex;
+}
+.center {
+  justify-content: center;
+  align-items: center;
+  text-align: center;
+}
+a {
+  text-decoration: none;
+}
+
+/** element-plus **/
+.el-icon{
+  text-align: center;
+}
+
+/** 用于提示信息 **/
+.my-tip {
+  background-color: #f1f1f1;
+  padding: 5px 10px;
+  text-align: left;
+  border-radius: 4px;
+}
+.system-scrollbar {
+  &::-webkit-scrollbar {
+    display: none;
+    width: 6px;
+  }
+  &::-webkit-scrollbar-thumb {
+    border-radius: 10px;
+    background: rgba(144, 147, 153, 0.3);
+  }
+  &:hover {
+    &::-webkit-scrollbar {
+      display: block;
+    }
+    &::-webkit-scrollbar-thumb {
+      border-radius: 10px;
+      background: rgba(144, 147, 153, 0.3);
+      &:hover {
+        background: rgba(144, 147, 153, 0.5);
+      }
+    }
+  }
+ 
+}

+ 37 - 0
src/assets/style/transition.scss

@@ -0,0 +1,37 @@
+/* fade-transform */
+.fade-transform-leave-active,
+.fade-transform-enter-active {
+  transition: all .2s;
+}
+
+.fade-transform-enter-from {
+  opacity: 0;
+  transform: translateX(-30px);
+  transition: all .2s;
+}
+
+.fade-transform-leave-to {
+  opacity: 0;
+  transform: translateX(30px);
+  transition: all .2s;
+}
+
+/* breadcrumb transition */
+.breadcrumb-enter-active,
+.breadcrumb-leave-active {
+  transition: all .2s;
+}
+
+.breadcrumb-enter,
+.breadcrumb-leave-active {
+  opacity: 0;
+  transform: translateX(80px);
+}
+
+.breadcrumb-move {
+  transition: all .5s;
+}
+
+.breadcrumb-leave-active {
+  position: absolute;
+}

+ 16 - 0
src/components/imgPreview/index.js

@@ -0,0 +1,16 @@
+import { createApp } from 'vue'
+import index from './index.vue'
+
+export default (options) => {
+    // 创建一个节点,并将组件挂载上去
+    const root = document.createElement('div')
+    document.body.appendChild(root)
+
+    const app = createApp(index, {
+        ...options, visible: true, remove() {
+            app.unmount(root) //创建完后要进行销毁
+            document.body.removeChild(root)
+        }
+    })
+    return app.mount(root)
+}

+ 26 - 0
src/components/imgPreview/index.vue

@@ -0,0 +1,26 @@
+<template>
+  <el-image-viewer v-if="show" v-bind="$attrs" hide-on-click-modal @close="show = false" />
+</template>
+
+<script setup>
+import { ref, watch } from "vue"
+import { ElImageViewer } from "element-plus" //自定义函数组件无法使用全局组件,需要单独引入
+
+const props = defineProps({
+  visible: {
+      type: Boolean,
+      default: false,
+  },
+  remove: {
+      type: Function, //传入createApp中移除节点的方法
+      default: null,
+  },
+  // api文档:https://element-plus.org/zh-CN/component/image.html#image-viewer-attributes
+})
+
+const show = ref(props.visible)
+// 监听显示的消失,需要移除dom
+watch(() => show.value, (val) => {
+  !val && props.remove()
+})
+</script>

+ 12 - 0
src/components/menu/index.vue

@@ -0,0 +1,12 @@
+<template>
+  <router-view></router-view>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+export default defineComponent({
+  setup() {
+
+  }
+})
+</script>

+ 102 - 0
src/components/table/index.vue

@@ -0,0 +1,102 @@
+<template>
+  <div class="system-table-box">
+    <el-table
+      v-bind="$attrs"
+      class="system-table"
+      border
+      height="100%"
+      :data="data"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" align="center" width="50" v-if="showSelection" />
+      <el-table-column label="序号" width="60" align="center" v-if="showIndex">
+        <template #default="scope">
+          {{ (page.index - 1) * page.size + scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <slot></slot>
+    </el-table>
+    <el-pagination
+      v-if="showPage"
+      v-model:current-page="page.index"
+      class="system-page"
+      background
+      :layout="pageLayout"
+      :total="page.total"
+      :page-size="page.size"
+      :page-sizes="pageSizes"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange"
+    >
+    </el-pagination>
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+export default defineComponent({
+  props: {
+    data: { type: Array, default: () => [] }, // 数据源
+    select: { type: Array, default: () => [] }, // 已选择的数据,与selection结合使用
+    showIndex: { type: Boolean, default: false }, // 是否展示index选择,默认否
+    showSelection: { type: Boolean, default: false }, // 是否展示选择框,默认否
+    showPage: { type: Boolean, default: true }, // 是否展示页级组件,默认是
+    page: { // 分页参数
+      type: Object,
+      default: () => {
+        return { index: 1, size: 20, total: 0 }
+      }
+    },
+    pageLayout: { type: String, default: "total, sizes, prev, pager, next, jumper" }, // 分页需要显示的东西,默认全部
+    pageSizes: { type: Array, default: [10, 20, 50, 100] }
+  },
+  setup(props, context) {
+    let timer = null
+    // 分页相关:监听页码切换事件
+    const handleCurrentChange = (val) => {
+      if (timer) {
+        props.page.index = 1
+      } else {
+        props.page.index = val
+        context.emit("getTableData")
+      }
+    }
+    // 分页相关:监听单页显示数量切换事件
+    const handleSizeChange = (val) => {
+      timer = 'work'
+      setTimeout(() => {
+        timer = null
+      }, 100)
+      props.page.size = val
+      context.emit("getTableData", true)
+    }
+    // 选择监听器
+    const handleSelectionChange = (val) =>{
+      context.emit("selection-change", val)
+    }
+    return {
+      handleCurrentChange,
+      handleSizeChange,
+      handleSelectionChange
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  .system-table-box {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: flex-start;
+    height: 100%;
+    .system-table {
+      flex: 1;
+      height: 100%;
+    }
+    
+    .system-page {
+      margin-top: 20px;
+    }
+  }
+</style>

+ 192 - 0
src/components/wangEditor/BasicEditor.vue

@@ -0,0 +1,192 @@
+<template>
+    <div>
+        <div style="border: 1px solid #ccc; margin-top: 10px;">
+            <Toolbar
+                :editor="editorRef"
+                :defaultConfig="toolbarConfig"
+                :mode="mode"
+                style="border-bottom: 1px solid #ccc"
+            />
+            <Editor
+                :defaultConfig="editorConfig"
+                :mode="mode"
+                v-model="valueHtml"
+                style="height: 400px; overflow-y: hidden;"
+                @onCreated="handleCreated"
+                @onChange="handleChange"
+                @onDestroyed="handleDestroyed"
+                @onFocus="handleFocus"
+                @onBlur="handleBlur"
+                @customAlert="customAlert"
+            />
+        </div>
+        <Button icon="md-add" size="small" @click="displayCodeState = !displayCodeState">显示代码视图</Button>
+        <div style="margin-top: 10px;" v-if="displayCodeState">
+            <textarea v-model="valueHtml" readonly style="width: 100%; height: 200px; outline: none;"></textarea>
+        </div>
+    </div>
+</template>
+
+<script>
+import '@wangeditor/editor/dist/css/style.css'
+import { onBeforeUnmount, ref, shallowRef, onMounted } from 'vue'
+import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
+import api from '@/request/apiConfig' //真实接口配置\
+import {compressFileM} from '@/utils/system/uploadCompress.js'
+
+export default {
+  components: { Editor, Toolbar },
+  setup() {
+    // 编辑器实例,必须用 shallowRef,重要!
+    const editorRef = shallowRef()
+
+    // 内容 HTML
+    const valueHtml = ref('')
+
+    // 显示代码视图开关
+    const displayCodeState = ref(false)
+
+    const toolbarConfig = {}
+    const editorConfig = { 
+        placeholder: '请输入内容...',
+        MENU_CONF: {
+            // 上传图片配置
+            uploadImage: {
+                server: api.apiManager + '/manager/images/upload', // 上传图片地址
+                timeout: 60 * 1000, // 60s
+                fieldName: 'file',
+                headers: {'X-AIYANGNIU-SIGNATURE':localStorage.aynUserToken},
+                maxFileSize: 10240 * 1024 * 1024, // 10G
+
+                onBeforeUpload(file) {
+                    return file
+                    // return false 会阻止上传
+                },
+                onSuccess(file, res) {
+                    // console.log('onSuccess', file, res)
+                },
+                // 编辑器自身原因,无法识别上传成功,放在failed里执行
+                onFailed(file, res) {
+
+                },
+                onError(file, err, res) {
+                    alert(err.message)
+                },
+
+                customInsert(res, insertFn) {
+                    insertFn(res.data, '图片', '')
+                },
+            },
+            // 上传视频配置
+            uploadVideo:{
+                onInsertedVideo(videoNode) {
+                    console.log('inserted video', videoNode)
+                },
+                server: api.apiManager + '/manager/images/upload', // 上传图片地址
+                timeout: 6000 * 1000, // 6000s
+                fieldName: 'file',
+                headers: {'X-AIYANGNIU-SIGNATURE':localStorage.aynUserToken},
+                maxFileSize: 5120 * 1024 * 1024, // 5G
+
+                onBeforeUpload(file) {
+                    console.log('onBeforeUpload', file)
+                    return file // will upload this file
+                },
+                onSuccess(file, res) {
+                    console.log('onSuccess', file, res)
+                },
+                onFailed(file, res) {
+                    console.log('onFailed', file, res)
+                },
+                onError(file, err, res) {
+                    alert(err.message)
+                    console.error('onError', file, err, res)
+                },
+                customInsert(res, insertFn) {
+                    insertFn(res.data, '')
+                },
+            }
+        }
+    }
+
+    // 组件销毁时,也及时销毁编辑器,重要!
+    onBeforeUnmount(() => {
+        const editor = editorRef.value
+        if (editor == null) return
+
+        editor.destroy()
+    })
+
+    // 编辑器回调函数
+    const handleCreated = (editor) => {
+      console.log('created', editor);
+      editorRef.value = editor // 记录 editor 实例,重要!
+    }
+    const handleChange = (editor) => {
+    //   console.log('change:', editor.getHtml());
+    }
+    const handleDestroyed = (editor) => {
+    //   console.log('destroyed', editor)
+    }
+    const handleFocus = (editor) => {
+        // console.log('focus', editor)
+    }
+    const handleBlur = (editor) => {
+        // console.log('blur', editor)
+    }
+    const customAlert = (info, type) => {
+        alert(`【自定义提示】${type} - ${info}`)
+    }
+
+    // 自动贴入内容
+    const customPaste = (editor, event, callback) => {
+        console.log('ClipboardEvent 粘贴事件对象',editor, event)
+
+        // 自定义插入内容
+        editor.insertText('xxx')
+
+        // 返回值(注意,vue 事件的返回值,不能用 return)
+        callback(false) // 返回 false ,阻止默认粘贴行为
+        // callback(true) // 返回 true ,继续默认的粘贴行为
+    }
+
+    const insertText = () => {
+        const editor = editorRef.value
+        if (editor == null) return
+        editor.insertText('hello world')
+    }
+
+    const printHtml = () => {
+        const editor = editorRef.value
+        if (editor == null) return
+        console.log(editor.getHtml())
+    }
+    
+    // 禁用富文本
+    const disable = () => {
+        const editor = editorRef.value
+        if (editor == null) return
+        editor.disable()
+    }
+
+    return {
+      editorRef,
+      mode: 'default',
+      valueHtml,
+      toolbarConfig,
+      editorConfig,
+      handleCreated,
+      handleChange,
+      handleDestroyed,
+      handleFocus,
+      handleBlur,
+      customAlert,
+      customPaste,
+      insertText,
+      printHtml,
+      disable,
+      displayCodeState,
+    };
+  }
+}
+</script>

+ 5 - 0
src/config/index.js

@@ -0,0 +1,5 @@
+const showLogo = true; // 是否显示Logo顶部模块
+const systemTitle = '牛只交易管理系统' // 系统名称,用于显示在左上角模块,以及浏览器标题上使用,使用配置项
+export {
+  systemTitle
+}

+ 61 - 0
src/layout/Header/Breadcrumb.vue

@@ -0,0 +1,61 @@
+<template>
+  <el-breadcrumb class="app-breadcrumb hidden-sm-and-down" separator="/">
+    <transition-group appear name="breadcrumb">
+      <el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
+        <span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{  item.meta.title }}</span>
+        <a v-else @click.prevent="handleLink(item)">
+          {{ item.meta.title }}
+        </a>
+      </el-breadcrumb-item>
+    </transition-group>
+  </el-breadcrumb>
+</template>
+
+<script>
+import { defineComponent, ref, watch } from "vue";
+import { useRoute, useRouter } from "vue-router";
+export default defineComponent({
+  name: "BreadCrumb",
+  setup() {
+    const levelList = ref([]);
+    const route = useRoute();
+    const router = useRouter();
+    const getBreadcrumb = () => {
+      let matched = route.matched.filter(item => item.meta && item.meta.title);
+      const first = matched[0];
+      levelList.value = matched.filter(
+        item => item.meta && item.meta.title && item.meta.breadcrumb !== false
+      );
+    };
+    getBreadcrumb();
+    watch(
+      () => route.path,
+      () => getBreadcrumb()
+    );
+    const handleLink = (item) => {
+      const { redirect, path } = item;
+      if (redirect) {
+        router.push(redirect.toString());
+        return;
+      }
+      router.push(path);
+    };
+    return { levelList, handleLink };
+  }
+});
+</script>
+
+<style lang="scss" scoped >
+.app-breadcrumb.el-breadcrumb {
+  display: inline-block;
+  font-size: 14px;
+  line-height: 50px;
+  .no-redirect {
+    color: var(--system-header-breadcrumb-text-color);
+    cursor: text;
+  }
+  a {
+    color: var(--system-header-text-color);
+  }
+}
+</style>

+ 31 - 0
src/layout/Header/functionList/fullscreen.vue

@@ -0,0 +1,31 @@
+<template>
+  <div :title="isFullscreen ? '退出全屏' : '全屏'">
+    <i class="iconfont" :class="isFullscreen ? 'icon-tuichuquanping':'icon-quanping1'" @click="toggle"></i>
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { useFullscreen } from '@vueuse/core'
+
+export default defineComponent({
+  name: 'fullscreen',
+  setup() {
+    const { isFullscreen, toggle } = useFullscreen()
+    return {
+      isFullscreen,
+      toggle
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  i {
+    cursor: pointer;
+    font-size: 18px;
+    &:focus {
+      outline: none;
+    }
+  }
+</style>

+ 62 - 0
src/layout/Header/functionList/sizeChange.vue

@@ -0,0 +1,62 @@
+<template>
+  <el-dropdown @command="handleCommand">
+    <div class="el-dropdown-link">
+      <i class="iconfont icon-fontsize"></i>
+    </div>
+    <template #dropdown>
+      <el-dropdown-menu>
+        <el-dropdown-item
+          v-for="d in list"
+          :key="d.size"
+          :command="d.size"
+          :disabled=" elementSize === d.size "
+        >
+          {{ d.name }}
+        </el-dropdown-item>
+      </el-dropdown-menu>
+    </template>
+  </el-dropdown>
+</template>
+
+<script>
+import { defineComponent, computed, unref } from 'vue'
+import { useRoute } from 'vue-router'
+import { useStore } from 'vuex'
+export default defineComponent({
+  setup() {
+    const store = useStore()
+    const route = useRoute()
+    const elementSize = computed(() => store.state.app.elementSize)
+    const list = [
+      { size: 'default', name: '默认' },
+      { size: 'large ', name: '大' },
+      { size: 'small', name: '小' },
+    ]
+    const { fullPath } = unref(route)
+    return {
+      list,
+      elementSize,
+      fullPath
+    }
+  },
+  methods: {
+    handleCommand(command) {
+      this.$store.commit('app/stateChange', {
+        name: 'elementSize',
+        value: command
+      })
+      this.setElementSize()
+    },
+    setElementSize() {
+      this.$ELEMENT.size = this.elementSize
+      this.$router.replace({
+        path: "/redirect" + this.fullPath
+      })
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  
+</style>

+ 173 - 0
src/layout/Header/functionList/theme.vue

@@ -0,0 +1,173 @@
+<template>
+  <div title="系统设置" @click="drawerChange(true)">
+    <i class="iconfont icon-shezhi"></i>
+  </div>
+  <el-drawer
+    title="系统设置"
+    v-model="drawer"
+    size="300px"
+    :show-close="false"
+    direction="rtl">
+      <h3>整体风格设置</h3>
+      <div class="theme-box">
+        <theme-icon
+          v-model:active="state.style"
+          v-for="(row, index) in style"
+          :key="index"
+          :name="index"
+          :tip="row.name"
+          :logo="row.logo.background"
+          :menu="row.menu.background"
+          :header="row.header.background"
+          :main="row.container.background"
+          :activeColor="row.page.color"
+        ></theme-icon>
+      </div>
+      <h3>主题色</h3>
+      <div class="theme-box">
+        <theme-color v-for="(item, key) in themeColorArr"
+          v-model:active="state.primaryColor"
+          v-model:activeTextColor="state.primaryTextColor"
+          :key="key"
+          :color="item.color"
+          :textColor="item.textColor"
+          :tip="item.tip">
+        </theme-color>
+      </div>
+      <h3>其他设置</h3>
+      <div class="list">
+        <div class="list-item" v-for="option in options" :key="option.name">
+          <span>{{ option.name }}</span>
+          <el-switch
+            v-model="option.value"
+            active-color="#13ce66"
+            inactive-color="#ff4949"
+            @change="change(option)"
+          >
+          </el-switch>
+        </div>
+      </div>
+  </el-drawer>
+</template>
+
+<script>
+import { defineComponent, ref, reactive, computed, watch } from 'vue'
+import { useStore } from 'vuex'
+import themeIcon from './theme/theme-icon.vue'
+import themeColor from './theme/theme-color.vue'
+import { style } from '@/theme/index'
+
+export default defineComponent({
+  components: {
+    themeIcon,
+    themeColor
+  },
+  setup() {
+    const store = useStore()
+    // 只取值,不做computed
+    const state = reactive({
+      style: store.state.app.theme.state.style,
+      primaryColor: store.state.app.theme.state.primaryColor,
+      primaryTextColor: store.state.app.theme.state.primaryTextColor,
+      menuType: store.state.app.theme.state.menuType
+    })
+    const themeColorArr = [
+      { color: '#409eff', textColor: '#fff', tip: '默认蓝' },
+      { color: '#d60f20', textColor: '#fff', tip: '玫瑰红' },
+      { color: '#ac25e6', textColor: '#fff', tip: '优雅紫' },
+      { color: '#4dc86f', textColor: '#fff', tip: '故事绿' },
+      { color: '#13c2c2', textColor: '#fff', tip: '明青' },
+      { color: '#333', textColor: '#fff', tip: '极客黑' }
+    ]
+    const setTheme = () => {
+      const userTheme = style[state.style]
+      const body = document.getElementsByTagName('body')[0]
+      // 设置全局顶部body上的class名称,即为主题名称,便于自定义配置符合当前主题的样式统一入口
+      body.className = state.style
+      // 需要设置的颜色参照theme.scss,位置:assets/style/theme.scss
+      // 设置主题色
+      body.style.setProperty('--system-primary-color', state.primaryColor)
+      for (let i in userTheme) {
+        const item = userTheme[i]
+        for (let y in item) {
+          let cssVarName = '--system-' + i + '-' + y.replace(/([A-Z])/g, "-$1").toLowerCase()
+          body.style.setProperty(cssVarName, item[y])
+        }
+      }
+    }
+    // 监听数据的变化
+    watch(state, (newVal) => {
+      const theme = {
+        state: {
+          ...state
+        }
+      }
+      store.commit('app/stateChange', {
+        name: 'theme',
+        value: theme
+      })
+      setTheme()
+    })
+    let drawer = ref(false)
+    const options = reactive([
+      { name: '是否显示LOGO', value: store.state.app.showLogo, store: 'showLogo' },
+      { name: '显示面包屑导航', value: store.state.app.showTabs, store: 'showTabs' },
+      { name: '保持一个菜单展开', value: store.state.app.expandOneMenu, store: 'expandOneMenu' }
+    ])
+    const drawerChange = (value) => {
+      drawer.value = value
+    }
+    const change = (option) => {
+      store.commit(`app/stateChange`, { name: option.store, value: option.value })
+    }
+    setTheme()
+    return {
+      drawer,
+      options,
+      state,
+      style,
+      themeColorArr,
+      drawerChange,
+      change
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  i {
+    cursor: pointer;
+    &:focus {
+      outline: none;
+    }
+  }
+  
+  .list {
+    padding: 0 20px;
+    &-item {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 15px;
+      span {
+        font-size: 16px;
+      }
+    }
+  }
+  h3 {
+    margin-top: 40px;
+    margin-bottom: 20px;
+    color: rgba(0,0,0,.85);
+    font-size: 14px;
+    line-height: 22px;
+    text-align: left;
+    padding: 0 20px;
+    &:first-child {
+      margin-top: 0;
+    }
+  }
+  .theme-box {
+    text-align: left;
+    padding-left: 20px;
+  }
+</style>

+ 77 - 0
src/layout/Header/functionList/theme/theme-color.vue

@@ -0,0 +1,77 @@
+<template>
+  <el-tooltip class="item" effect="dark" :content="tip" placement="top">
+    <div class="theme-color" :style="{ 'background-color': color }" @click="handleClick">
+      <div class="active" v-if="active === color">
+        <i class="el-icon-check" :style="{'color': textColor}"></i>
+      </div>
+    </div>
+  </el-tooltip>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+export default defineComponent({
+  props: {
+    active: {
+      type: String,
+      default: ''
+    },
+    activeTextColor: {
+      type: String,
+      default: ''
+    },
+    tip: {
+      type: String,
+      default: '默认蓝'
+    },
+    color: {
+      type: String,
+      default: '#409eff'
+    },
+    textColor: {
+      type: String,
+      default: '#fff'
+    }
+  },
+  setup(props, ctx) {
+    // 点击事件,触发v-model修改active值
+    const handleClick = () => {
+      ctx.emit('update:active', props.color)
+      ctx.emit('update:activeTextColor', props.textColor)
+    }
+    return {
+      handleClick
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  .theme-color {
+    border-radius: 4px;
+    width: 20px;
+    height: 20px;
+    display: inline-block;
+    cursor: pointer;
+    outline: none;
+    position:relative;
+    .active {
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      top: 0;
+      left: 0;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      i {
+        color: #fff;
+        font-weight: bold;
+        font-size: 12px;
+      }
+    }
+    &+.theme-color{
+      margin-left: 10px;
+    }
+  }
+</style>

+ 130 - 0
src/layout/Header/functionList/theme/theme-icon.vue

@@ -0,0 +1,130 @@
+<template>
+  <el-tooltip class="item" effect="dark" :content="tip" placement="top">
+    <div class="theme-icon" @click="handleClick">
+      <div class="theme-icon-sidebar">
+        <div class="theme-icon-sidebar-logo" :style="{ 'background-color': logo }"></div>
+        <div class="theme-icon-sidebar-menu" :style="{ 'background-color': menu }"></div>
+      </div>
+      <div class="theme-icon-content">
+        <div class="theme-icon-content-header" :style="{ 'background-color': header }"></div>
+        <div class="theme-icon-content-main" :style="{ 'background-color': main }">
+          <div class="active" v-if="active === name">
+            <i class="el-icon-check" :style="{'color': activeColor}"></i>
+          </div>
+        </div>
+      </div>
+      
+    </div>
+  </el-tooltip> 
+  
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+export default defineComponent({
+  props: {
+    name: {
+      type: String,
+      default: 'default'
+    },
+    active: {
+      type: String,
+      default: ''
+    },
+    menu: {
+      type: String,
+      defualt: ''
+    },
+    logo: {
+      type: String,
+      defualt: ''
+    },
+    header: {
+      type: String,
+      defualt: ''
+    },
+    main: {
+      type: String,
+      defualt: ''
+    },
+    tip: {
+      type: String,
+      default: '默认菜单风格'
+    },
+    activeColor: {
+      type: String,
+      default: ''
+    }
+  },
+  setup(props, ctx) {
+    // 点击事件,触发v-model修改active值
+    const handleClick = () => {
+      ctx.emit('update:active', props.name)
+    }
+    return {
+      handleClick
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  .theme-icon {
+    display: inline-flex;
+    width: 50px;
+    height: 50px;
+    box-shadow: 0 1px 2.5px 0 rgba(0,0,0,.18);
+    cursor: pointer;
+    box-shadow: 0 2px 4px rgba(0,0,0,0.12),0 0 6px rgba(0,0,0,0.04);
+    outline: none;
+    border-radius: 4px;
+    overflow: hidden;
+    &-sidebar{
+      width: 18px;
+      display: flex;
+      flex-direction: column;
+      &-logo{
+        width: 20px;
+        height: 10px;
+        background-color: #263445;
+      }
+      &-menu{
+        flex: 1;
+        background-color: rgb(40, 65, 90);
+      }
+    }
+    &-content{
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      &-header{
+        height: 10px;
+        background-color: #fff;
+      }
+      &-main{
+        flex: 1;
+        background-color: #f8f8f8;
+        position: relative;
+        .active {
+          position: absolute;
+          width: 100%;
+          height: 100%;
+          top: 0;
+          left: 0;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          i {
+            color: #000;
+            font-weight: bold;
+            font-size: 22px;
+          }
+        }
+      }
+    }
+
+  }
+  .theme-icon+.theme-icon{
+    margin-left: 20px;
+  }
+</style>

+ 152 - 0
src/layout/Header/index.vue

@@ -0,0 +1,152 @@
+<template>
+  <header>
+    <div class="left-box">
+      <!-- 收缩按钮 -->
+      <div class="menu-icon" @click="opendStateChange">
+        <i class="iconfont" :class="isCollapse ? 'icon-caidanzhankai':'icon-caidanshouqi'" style="font-size:24px;"></i>
+      </div>
+      <Breadcrumb />
+    </div>
+    <div class="right-box">
+      <!-- 快捷功能按钮 -->
+      <div class="function-list">
+        <div class="function-list-item hidden-sm-and-down">
+          <Full-screen />
+        </div>
+        <div class="function-list-item"><SizeChange /></div>
+        <div class="function-list-item hidden-sm-and-down"><Theme /></div>
+      </div>
+      <!-- 用户信息 -->
+      <div class="user-info">
+        <el-avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" style="margin-top:1px;" size="small"/>
+        <el-dropdown>
+          <div class="el-dropdown-link">
+            <span>{{userInfo.nickName || '请登录'}}</span> 
+            <i class="iconfont icon-xialazhankai" style="font-size:14px;margin-left: 3px;"></i>
+          </div>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item @click="loginOut">退出登录</el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+      </div>
+    </div>
+  </header>
+</template>
+
+<script>
+import { defineComponent, computed, reactive } from 'vue'
+import { useStore } from 'vuex'
+import { useRouter, useRoute } from 'vue-router'
+import FullScreen from './functionList/fullscreen.vue'
+import SizeChange from './functionList/sizeChange.vue'
+import Theme from './functionList/theme.vue'
+import Breadcrumb from './Breadcrumb.vue'
+import { ElMessage } from 'element-plus'
+export default defineComponent({
+  components: {
+    FullScreen,
+    Breadcrumb,
+    SizeChange,
+    Theme,
+  },
+  setup() {
+    const store = useStore()
+    const router = useRouter()
+    const route = useRoute()
+    const layer = reactive({
+      show: false,
+      showButton: true
+    })
+
+    const userInfo = computed(()=>store.state.user.userInfo)
+    const isCollapse = computed(() => store.state.app.isCollapse)
+    // isCollapse change to hide/show the sidebar
+    const opendStateChange = () => {
+      store.commit('app/isCollapseChange', !isCollapse.value)
+    }
+
+    // login out the system
+    const loginOut = () => {
+      store.dispatch('user/loginOut').then(() => {
+        ElMessage.success({
+          message: '退出登录成功',
+          type: 'success',
+          showClose: true,
+          duration: 1000
+        })
+        router.push('/login')
+      })
+    }
+    
+  
+    return {
+      isCollapse,
+      layer,
+      opendStateChange,
+      loginOut,
+      userInfo,
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 60px;
+    background-color: var(--system-header-background);
+    padding-right: 22px;
+  }
+  .left-box {
+    height: 100%;
+    display: flex;
+    align-items: center;
+    border-left: 1px solid var(--system-header-border-color);
+    .menu-icon {
+      width: 60px;
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 25px;
+      font-weight: 100;
+      cursor: pointer;
+      margin-right: 10px;
+      &:hover {
+        background-color: var(--system-header-item-hover-color);
+      }
+      i {
+        color: var(--system-header-text-color);
+      }
+    }
+  }
+  .right-box {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    .function-list{
+      display: flex;
+      .function-list-item {
+        width: 30px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        :deep(i) {
+          color: var(--system-header-text-color);
+        }
+      }
+    }
+    .user-info {
+      margin-left: 20px;
+      .el-dropdown-link {
+        margin-top: 8px;
+        margin-left: 5px;
+        color: var(--system-header-breadcrumb-text-color);
+      }
+    }
+  }
+</style>

+ 47 - 0
src/layout/Logo/index.vue

@@ -0,0 +1,47 @@
+<template>
+  <div class="logo-container">
+    <Row>
+      <Col :span="isCollapse?24:5" class="login-posi">
+        <img src="@/assets/AYN.png" style="width:70%;" alt="">
+      </Col>
+      <Col span="19" v-if="!isCollapse">
+        <h1>{{ systemTitle }}</h1>
+      </Col>
+    </Row>
+  </div>
+</template>
+
+<script>
+import { defineComponent, computed } from 'vue'
+import { useStore } from 'vuex'
+import { systemTitle } from '@/config'
+export default defineComponent({
+  setup() {
+    const store = useStore()
+    const isCollapse = computed(() => store.state.app.isCollapse)
+    return {
+      isCollapse,
+      systemTitle
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  .logo-container {
+    height: 60px;
+    width: 100%;
+    background-color: var(--system-logo-background);
+    .login-posi{
+      height: 60px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+    h1 {
+      font-size: 20px;
+      white-space: nowrap;
+      color: var(--system-logo-color);
+    }
+  }
+</style>

+ 41 - 0
src/layout/Menu/Link.vue

@@ -0,0 +1,41 @@
+<template>
+  <component :is="type" v-bind="linkProps(to)" @click="hideMenu" >
+    <slot>
+    </slot>
+  </component>
+</template>
+<script>
+import { defineComponent, computed } from 'vue'
+import { useStore } from "vuex";
+export default defineComponent({
+  name: 'appLink',
+  props: {
+    to: {
+      type: String,
+      required: true
+    }
+  },
+  setup(props) {
+    const store = useStore();
+    const isCollapse = computed(() => store.state.app.isCollapse);
+    const linkProps = (to) => {
+     return {
+       to: to
+     } 
+    }
+    const hideMenu = () => {
+      if (document.body.clientWidth <= 1000 && !isCollapse.value) {
+        store.commit("app/isCollapseChange", true);
+      }
+    };
+    return {
+      type: "router-link",
+      linkProps,
+      hideMenu
+    }
+  }
+})
+</script>
+<style lang="">
+  
+</style>

+ 100 - 0
src/layout/Menu/MenuItem.vue

@@ -0,0 +1,100 @@
+<template>
+  <template v-if="!menu.hideMenu">
+    <el-sub-menu v-if="showMenuType === 2" :index="pathResolve">
+      <template #title v-if="!menu.meta.hideMenuItem">
+        <i :class="'iconfont'+' '+menu.meta.icon" v-if="menu.meta.icon"></i>
+        <span class="marginLeft5">{{ menu.meta.title }}</span>
+      </template>
+      <menu-item v-for="(item, key) in menu.children" :key="key" :menu="item" :basePath="pathResolve" />
+    </el-sub-menu>
+    <app-link v-else-if="showMenuType === 1" :to="pathResolve">
+      <el-menu-item :index="pathResolve" v-if="(!menu.children[0].children || menu.children[0].children.length === 0)&&!menu.meta.hideMenuItem">
+        <i :class="'iconfont'+' '+menu.children[0].meta.icon || menu.meta.icon" v-if="menu.children[0].meta.icon || menu.meta.icon"></i>
+        <template #title><span class="marginLeft5">{{ menu.children[0].meta.title }}</span></template>
+      </el-menu-item>
+      <el-sub-menu v-else :index="pathResolve">
+        <template #title v-if="!menu.meta.hideMenuItem">
+          <i :class="'iconfont'+' '+menu.children[0].meta.icon || menu.meta.icon" v-if="menu.children[0].meta.icon || menu.meta.icon"></i>
+          <span class="marginLeft5">{{ menu.children[0].meta.title }}</span>
+        </template>
+        <menu-item v-for="(item, key) in menu.children[0].children" :key="key" :menu="item" :basePath="pathResolve" />
+      </el-sub-menu>
+    </app-link>
+    <app-link v-else :to="pathResolve">
+      <el-menu-item :index="pathResolve" v-if="!menu.meta.hideMenuItem">
+        <i :class="'iconfont'+' '+menu.meta.icon" v-if="menu.meta.icon"></i>
+        <template #title><span class="marginLeft5">{{ menu.meta.title }}</span></template>
+      </el-menu-item>
+    </app-link>
+  </template>
+</template>
+
+<script>
+import { defineComponent, computed } from 'vue'
+import appLink from './Link.vue'
+export default defineComponent({
+  name: 'menu-item',
+  props: {
+    menu: {
+      type: Object,
+      required: true
+    },
+    basePath: {
+      type: String,
+      default: ''
+    }
+  },
+  components: {
+    appLink
+  },
+
+  setup(props) {
+    const menu = props.menu
+    // todo: 优化if结构
+    const showMenuType = computed(() => { // 0: 无子菜单, 1:有1个子菜单, 2:显示上下级子菜单
+      if (menu.children && (menu.children.length > 1 || (menu.children.length === 1 && menu.alwayShow))) {
+        return 2
+      } else if (menu.children && menu.children.length === 1 && !menu.alwayShow) {
+        return 1
+      } else {
+        return 0
+      }
+    })
+    // todo: 优化多层if
+    const pathResolve = computed(() => {
+      let path = ''
+      if (showMenuType.value === 1) {
+        if (menu.children[0].path.charAt(0) === '/') {
+          path = menu.children[0].path
+        } else {
+          let char = '/'
+          if (menu.path.charAt(menu.path.length - 1) === '/') {
+            char = ''
+          }
+          path = menu.path + char + menu.children[0].path
+        }
+      } else {
+        path = menu.path
+      }
+      path = props.basePath ? props.basePath + '/' + path : path
+      return path
+    })
+    return {
+      showMenuType,
+      pathResolve
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  .el-sub-menu {
+    text-align: left;
+  }
+  .el-menu-item {
+    text-align: left;
+  }
+  .marginLeft5{
+    margin-left: 5px;
+  }
+</style>

+ 120 - 0
src/layout/Menu/index.vue

@@ -0,0 +1,120 @@
+<template>
+  <el-scrollbar>
+    <el-menu
+      class="layout-menu system-scrollbar"
+      background-color="var(--system-menu-background)"
+      text-color="var(--system-menu-text-color)"
+      active-text-color="var(--system-primary-color)"
+      :default-active="activeMenu"
+      :class="isCollapse? 'collapse': ''"
+      :collapse="isCollapse"
+      :collapse-transition="false"
+      :unique-opened="expandOneMenu"
+    >
+      <menu-item v-for="(menu, key) in allRoutes" :key="key" :menu="menu" />
+    </el-menu>
+  </el-scrollbar>
+</template>
+
+<script>
+import { defineComponent, computed, onMounted } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import { useStore } from 'vuex'
+import MenuItem from './MenuItem.vue'
+export default defineComponent({
+  components: {
+    MenuItem
+  },
+
+  setup() {
+    const store = useStore()
+    const isCollapse = computed(() => store.state.app.isCollapse)
+    const expandOneMenu = computed(() => store.state.app.expandOneMenu)
+    const allRoutes = useRouter().options.routes
+    const route = useRoute()
+    const activeMenu = computed(() => {
+      const { meta, path } = route;
+      if (meta.activeMenu) {
+        return meta.activeMenu;
+      }
+      return path;
+    });
+    
+    onMounted(() => {
+
+    })
+    return {
+      isCollapse,
+      expandOneMenu,
+      allRoutes,
+      activeMenu,
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  .el-scrollbar {
+    background-color: var(--system-menu-background);
+  }
+  .layout-menu {
+    border-right:0px solid;
+    width: 100%;
+    &.collapse {
+      margin-left: 0px;
+    }
+    :deep() {
+      .el-menu-item, .el-submenu {
+        background-color: var(--system-menu-background) !important;
+      }
+      .el-menu-item i, .el-menu-item-group__title, .el-submenu__title i {
+        color: var(--system-menu-text-color);
+      }
+      .el-menu-item, .el-submenu__title{
+        &.is-active {
+          background-color: var(--system-primary-color) !important;
+          color: var(--system-primary-text-color) !important;
+          i {
+            color: var(--system-primary-text-color) !important;
+          }
+          &:hover {
+            background-color: var(--system-primary-color) !important;
+            color: var(--system-primary-text-color) !important;
+          }
+        }
+        &:hover {
+          background-color: var(--system-menu-hover-background) !important;
+        }
+      }
+      .el-submenu {
+        &.is-active {
+          >.el-submenu__title, >.el-submenu__title i {
+            color: var(--system-menu-submenu-active-color) !important;
+          }
+        }
+        .el-menu-item {
+          background-color: var(--system-menu-children-background) !important;
+          &.is-active {
+            background-color: var(--system-primary-color) !important;
+            color: var(--system-primary-text-color) !important;
+            &:hover {
+              background-color: var(--system-primary-color) !important;
+              color: var(--system-primary-text-color) !important;
+            }
+          }
+          &:hover {
+            background-color: var(--system-menu-hover-background) !important;
+          }
+        }
+      }
+      .el-submenu {
+        .el-submenu__title {
+          background-color: var(--system-menu-children-background) !important;
+          &:hover {
+            background-color: var(--system-menu-hover-background) !important;
+          }
+        }
+      }
+    }
+  }
+</style>

+ 265 - 0
src/layout/Tabs/index.vue

@@ -0,0 +1,265 @@
+<template>
+  <div class="tabs">
+    <el-scrollbar class="scroll-container tags-view-container" ref="scrollbarDom">
+      <Item
+        v-for="menu in menuList"
+        :key="menu.meta.title"
+        :menu="menu"
+        :active="activeMenu.path === menu.path"
+        @close="delMenu(menu)"
+        @reload="pageReload"
+      />
+    </el-scrollbar>
+    <div class="handle">
+      <el-dropdown placement="bottom">
+        <div class="el-dropdown-link">
+          <i class="iconfont icon-xialajiantouxiao"></i>
+        </div>
+        <template #dropdown>
+          <el-dropdown-menu>
+            <el-dropdown-item @click="pageReload">重新加载</el-dropdown-item>
+            <el-dropdown-item :disabled="currentDisabled" @click="closeCurrentRoute">关闭当前标签</el-dropdown-item>
+            <el-dropdown-item :disabled="menuList.length < 3" @click="closeOtherRoute">关闭其他标签</el-dropdown-item>
+            <el-dropdown-item :disabled="menuList.length <= 1" @click="closeAllRoute">关闭所有标签</el-dropdown-item>
+          </el-dropdown-menu>
+        </template>
+      </el-dropdown>
+      <i class="iconfont" :class="contentFullScreen?'icon-tuichuquanping':'icon-quanping1'" @click="onFullscreen"></i>
+    </div>
+  </div>
+</template>
+
+<script>
+import { defineComponent, computed, unref, watch, reactive, ref, nextTick } from 'vue'
+import Item from './item.vue'
+import { useStore } from 'vuex'
+import { useRoute, useRouter } from 'vue-router'
+import tabsHook from './tabsHook'
+export default defineComponent({
+  components: {
+    Item
+  },
+  setup() {
+    const store = useStore()
+    const route = useRoute()
+    const router = useRouter()
+    const scrollbarDom = ref(null)
+    const allRoutes = router.options.routes
+    const defaultMenu = {
+      path: '/classifyManageIndex',
+      meta: { title: '牛只分类', hideClose: true }
+    }
+    const contentFullScreen = computed(() => store.state.app.contentFullScreen)
+    const currentDisabled = computed(() => route.path === defaultMenu.path)
+
+    let activeMenu = reactive({ path: '' })
+    let menuList = ref(tabsHook.getItem())
+    if (menuList.value.length === 0) { // 判断之前有没有调用过
+      addMenu(defaultMenu)
+    } 
+    watch(menuList.value, (newVal) => {
+      tabsHook.setItem(newVal)
+    })
+    watch(menuList, (newVal) => {
+      tabsHook.setItem(newVal)
+    })
+    router.afterEach(() => {
+      addMenu(route)
+      initMenu(route)
+    })
+
+    // 全屏
+    function onFullscreen() {
+      store.commit('app/contentFullScreenChange', !contentFullScreen.value)
+    }
+    // 当前页面组件重新加载
+    function pageReload() {
+      const self = route.matched[route.matched.length-1].instances.default
+      // console.log(route.matched);
+      
+      self.handleReload();
+      // const { fullPath, meta, name } = unref(route);
+      // if (meta.cache && name) {
+      //   store.commit('keepAlive/delKeepAliveComponentsName', name)
+      // }
+      // router.replace({
+      //   path: "/redirect" + fullPath
+      // });
+    }
+
+    // 关闭当前标签,首页不关闭
+    function closeCurrentRoute() {
+      if (route.path !== defaultMenu.path) {
+        delMenu(route)
+      }
+    }
+    // 关闭除了当前标签之外的所有标签
+    function closeOtherRoute() {
+      menuList.value = [defaultMenu]
+      if (route.path !== defaultMenu.path) {
+        addMenu(route)
+      }
+      setKeepAliveData()
+    }
+
+    // 关闭所有的标签,除了首页
+    function closeAllRoute() {
+      menuList.value = [defaultMenu]
+      setKeepAliveData()
+      router.push(defaultMenu.path)
+    }
+
+    // 添加新的菜单项
+    function addMenu(menu) {
+      let { path, meta, name } = menu
+      if (meta.hideTabs) {
+        return
+      }
+      let hasMenu = menuList.value.some((obj) => {
+        return obj.path === path
+      })
+      if (!hasMenu) {
+        menuList.value.push({
+          path,
+          meta,
+          name
+        })
+      }
+    }
+
+    // 删除菜单项
+    function delMenu(menu) {
+      let index = 0
+      if (!menu.meta.hideClose) {
+        if (menu.meta.cache && menu.name) {
+          store.commit('keepAlive/delKeepAliveComponentsName', menu.name)
+        }
+        index = menuList.value.findIndex((item) => item.path === menu.path)
+        menuList.value.splice(index, 1)
+      }
+      if (menu.path === activeMenu.path) {
+        index - 1 > 0 ? router.push(menuList.value[index - 1].path) : router.push(defaultMenu.path)
+      }
+    }
+
+    // 初始化activeMenu
+    function initMenu(menu) {
+      activeMenu = menu
+      nextTick(() => {
+        // setPosition()
+      })
+    }
+    // 设置当前滚动条应该在的位置
+    function setPosition() {
+      console.log('查看当前什么值',scrollbarDom);
+      if (scrollbarDom.value) {
+        const domBox = {
+          scrollbar: scrollbarDom.value.scrollbar.querySelector('.el-scrollbar__wrap '),
+          activeDom: scrollbarDom.value.scrollbar.querySelector('.active'),
+          activeFather: scrollbarDom.value.scrollbar.querySelector('.el-scrollbar__view')
+        }
+        for (let i in domBox) {
+          if (!domBox[i]) {
+            return
+          }
+        }
+        const domData = {
+          scrollbar: domBox.scrollbar.getBoundingClientRect(),
+          activeDom: domBox.activeDom.getBoundingClientRect(),
+          activeFather: domBox.activeFather.getBoundingClientRect()
+        }
+        const num = domData.activeDom.x - domData.activeFather.x + 1/2 * domData.activeDom.width - 1/2 * domData.scrollbar.width
+        domBox.scrollbar.scrollLeft = num
+      }
+    }
+
+    // 配置需要缓存的数据
+    function setKeepAliveData() {
+      let keepAliveNames = []
+      menuList.value.forEach((menu) => {
+        menu.meta && menu.meta.cache && menu.name && keepAliveNames.push(menu.name)
+      })
+      store.commit('keepAlive/setKeepAliveComponentsName', keepAliveNames)
+    }
+    
+    // 初始化时调用:1. 新增菜单 2. 初始化activeMenu
+    addMenu(route)
+    initMenu(route)
+    return {
+      contentFullScreen,
+      onFullscreen,
+      pageReload,
+      scrollbarDom,
+      // 菜单相关
+      menuList,
+      activeMenu,
+      delMenu,
+      closeCurrentRoute,
+      closeOtherRoute,
+      closeAllRoute,
+      currentDisabled
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  .tabs {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 40px;
+    background: var(--system-header-background);
+    border-bottom: 1px solid var(--system-header-border-color);
+    border-top: 1px solid var(--system-header-border-color);
+    border-left: 1px solid var(--system-header-border-color);
+    box-shadow: 0 1px 4px 0 rgba(0, 0, 0, .1);
+    .handle {
+      min-width: 95px;
+      height: 100%;
+      display: flex;
+      align-items: center;
+      .el-dropdown-link {
+        margin-top: 5px;
+        border-left: 1px solid var(--system-header-border-color);
+        height: 25px;
+        width: 40px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+      }
+      i {
+        color: var(--system-header-text-color);
+      }
+    }
+  }
+  .scroll-container {
+    white-space: nowrap;
+    position: relative;
+    overflow: hidden;
+    width: 100%;
+    :deep {
+      .el-scrollbar__bar {
+        bottom: 0px;
+      }
+      .el-scrollbar__wrap {
+        height: 49px;
+      }
+    }
+  }
+  .tags-view-container {
+    height: 34px;
+    flex: 1;
+    width: 100%;
+    display: flex;
+  }
+  .el-icon-full-screen {
+    cursor: pointer;
+    &:hover {
+      background: rgba(0,0,0,.025);
+    }
+    &:focus {
+      outline: none;
+    }
+  }
+</style>

+ 100 - 0
src/layout/Tabs/item.vue

@@ -0,0 +1,100 @@
+<template>
+  <div class="tags-view-item" :class="active? 'active' : ''">
+    <router-link :to="menu.path" v-if="menu.meta.title">
+      {{ menu.meta.title }}
+    </router-link>
+    <i class="iconfont icon-shuaxin"  @click.stop="reload" v-if="active" style="font-size:14px;"></i>
+    <i class="iconfont icon-guanbi1"  @click.stop="closeTab" v-if="!menu.meta.hideClose" alt="关闭当前标签页" style="font-size:14px;"></i>
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+export default defineComponent({
+  props: {
+    menu: {
+      type: Object,
+      default: () => {
+        return {
+          path: '',
+          meta: {
+            label: '',
+            hideClose: false
+          }
+        }
+      }
+    },
+    active: {
+      type: Boolean,
+      default: false
+    }
+  },
+  setup(props, { emit }) {
+    // 关闭按钮
+    function closeTab() {
+      emit('close')
+    }
+    // 刷新按钮
+    function reload() {
+      emit('reload')
+    }
+    return {
+      closeTab,
+      reload
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+  .tags-view-item {
+    display: inline-block;
+    position: relative;
+    cursor: pointer;
+    height: 24px;
+    line-height: 24px;
+    border: 1px solid var(--system-header-border-color);
+    color: var(--system-header-text-color);
+    background: var(--system-header-tab-background);
+    padding: 0 8px;
+    font-size: 12px;
+    margin-left: 5px;
+    margin-top: 4px;
+    border-radius: 2px;
+    a {
+      color: var(--system-header-text-color);
+      height: 26px;
+      display: inline-block;
+      padding-left: 8px;
+      padding-right: 8px;
+    }
+    .el-icon-refresh-right {
+      display: inline-block;
+      margin-right: 5px;
+    }
+    .el-icon-close {
+      display: inline-block;
+      height: 26px;
+    }
+    &:first-of-type {
+      margin-left: 15px;
+    }
+    &:last-of-type {
+      margin-right: 15px;
+    }
+    &.active {
+      background: var(--system-primary-color);
+      border-color: var(--system-primary-color);
+      color: var(--system-primary-text-color);
+      a {
+        color: var(--system-primary-text-color);
+      }
+      &:hover {
+        background: var(--system-primary-color);
+      }
+    }
+    &:hover {
+      background-color: var(--system-header-item-hover-color);
+    }
+  }
+</style>

+ 9 - 0
src/layout/Tabs/tabsHook.js

@@ -0,0 +1,9 @@
+const tabsHook = {
+  setItem: function(arr) {
+    localStorage.setItem('tabs', JSON.stringify(arr))
+  },
+  getItem: function() {
+    return JSON.parse(localStorage.getItem('tabs') || '[]')
+  }
+}
+export default tabsHook

+ 134 - 0
src/layout/index.vue

@@ -0,0 +1,134 @@
+<template>
+  <el-container style="height: 100vh">
+    <div class="mask" v-show="!isCollapse && !contentFullScreen" @click="hideMenu"></div>
+    <el-aside
+      :width="isCollapse ? '60px' : '250px'"
+      :class="isCollapse ? 'hide-aside' : 'show-side'"
+      v-show="!contentFullScreen"
+    >
+      <Logo v-if="showLogo" />
+      <Menu />
+    </el-aside>
+    <el-container>
+      <el-header v-show="!contentFullScreen">
+        <Header />
+      </el-header>
+      <Tabs v-show="showTabs" />
+      <el-main>
+        <router-view v-slot="{ Component, route }">
+          <transition :name="route.meta.transition || 'fade-transform'" mode="out-in">
+            <keep-alive v-if="keepAliveComponentsName" :include="keepAliveComponentsName">
+              <component :is="Component" :key="route.fullPath" />
+            </keep-alive>
+            <component v-else :is="Component" :key="route.fullPath" />
+          </transition>
+        </router-view>
+      </el-main>
+    </el-container>
+  </el-container>
+</template>
+
+<script>
+import { defineComponent, computed, onBeforeMount } from "vue";
+import { useStore } from "vuex";
+import { useRouter } from "vue-router";
+import { useEventListener } from "@vueuse/core";
+import Menu from "./Menu/index.vue";
+import Logo from "./Logo/index.vue";
+import Header from "./Header/index.vue";
+import Tabs from "./Tabs/index.vue";
+export default defineComponent({
+  components: {
+    Menu,
+    Logo,
+    Header,
+    Tabs,
+  },
+  setup() {
+    const store = useStore();
+    // computed
+    const isCollapse = computed(() => store.state.app.isCollapse);
+    const contentFullScreen = computed(() => store.state.app.contentFullScreen);
+    const showLogo = computed(() => store.state.app.showLogo);
+    const showTabs = computed(() => store.state.app.showTabs);
+    const keepAliveComponentsName = computed(() => store.getters['keepAlive/keepAliveComponentsName']);
+    // 页面宽度变化监听后执行的方法
+    const resizeHandler = () => {
+      if (document.body.clientWidth <= 1000 && !isCollapse.value) {
+        store.commit("app/isCollapseChange", true);
+      } else if (document.body.clientWidth > 1000 && isCollapse.value) {
+        store.commit("app/isCollapseChange", false);
+      }
+    };
+    // 初始化调用
+    resizeHandler();
+    // beforeMount
+    onBeforeMount(() => {
+      // 监听页面变化
+      useEventListener("resize", resizeHandler);
+    });
+    // methods
+    // 隐藏菜单
+    const hideMenu = () => {
+      store.commit("app/isCollapseChange", true);
+    };
+    return {
+      isCollapse,
+      showLogo,
+      showTabs,
+      contentFullScreen,
+      keepAliveComponentsName,
+      hideMenu,
+    };
+  },
+});
+</script>
+
+<style lang="scss" scoped>
+.el-header {
+  padding-left: 0;
+  padding-right: 0;
+}
+.el-aside {
+  display: flex;
+  flex-direction: column;
+  transition: 0.2s;
+  overflow-x: hidden;
+  transition: 0.3s;
+  &::-webkit-scrollbar {
+    width: 0 !important;
+  }
+}
+.el-main {
+  background-color: var(--system-container-background);
+  height: 100%;
+  padding: 0;
+}
+.el-main-box {
+  width: 100%;
+  height: 100%;
+  overflow-y: auto;
+  box-sizing: border-box;
+}
+@media screen and (max-width: 1000px) {
+  .el-aside {
+    position: fixed;
+    top: 0;
+    left: 0;
+    height: 100vh;
+    z-index: 1000;
+    &.hide-aside {
+      left: -250px;
+    }
+  }
+  .mask {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100vw;
+    height: 100vh;
+    z-index: 999;
+    background: rgba(0, 0, 0, 0.5);
+  }
+}
+</style>

+ 24 - 0
src/main.js

@@ -0,0 +1,24 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+import store from './store'
+import router from './router'
+// 引入iview
+import ViewUIPlus from 'view-ui-plus'
+import 'view-ui-plus/dist/styles/viewuiplus.css'
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css'
+import 'normalize.css' // css初始化
+import './assets/style/common.scss' // 公共css
+import '@/assets/iconfont/iconfont.css'
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+
+const app = createApp(App)
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+  app.component(key, component)
+}
+app.use(store)
+// app.config.performance = true
+app.use(ViewUIPlus)
+app.use(ElementPlus, { size: store.state.app.elementSize })
+
+app.use(router).mount('#app')

+ 15 - 0
src/request/api.js

@@ -0,0 +1,15 @@
+
+import * as apiUser from './modules/apiUser'    //用户服务系统
+import * as apiContent  from './modules/apiContent' //内容服务系统
+import * as apiCow  from './modules/apiCow' //牛只管理
+import * as apiOrder  from './modules/apiOrder' //牛只订单
+import * as apiEcontract  from './modules/apiEcontract'
+import * as apiItem  from './modules/apiItem'
+
+
+export const user = apiUser
+export const content  = apiContent
+export const cow  = apiCow
+export const order  = apiOrder
+export const econtract  = apiEcontract
+export const item  = apiItem

+ 37 - 0
src/request/apiConfig.js

@@ -0,0 +1,37 @@
+// 接口配置文件
+let env = process.env.NODE_ENV
+let api
+if (env === 'development') {  //开发环境
+  api = {
+    apiUser: 'http://account2.aiyangniu.net',
+    apiContent: 'http://contents2.aiyangniu.net',
+    apiManager: 'http://manager2.aiyangniu.net',
+    apiCow: 'http://cow.aiyangniu.net',
+    apiOrder: 'http://orders2.aiyangniu.net',
+    apiEcontract:'http://econtracts.aiyangniu.net',
+    apiItem: 'http://items2.aiyangniu.net',
+  }
+} else if (env === 'testing') {
+  api = {
+    apiUser: 'http://account2.aiyangniu.net',
+    apiContent: 'http://contents2.aiyangniu.net',
+    apiManager: 'http://manager2.aiyangniu.net', 
+    apiCow: 'http://cow.aiyangniu.net',
+    apiOrder: 'http://orders2.aiyangniu.net',
+    apiEcontract:'http://econtracts.aiyangniu.net',
+    apiItem: 'http://items2.aiyangniu.net',
+  }
+} else { //生产环境
+  api = {
+    apiUser: 'http://account.aiyangniu.cn',
+    apiContent: 'http://contents.aiyangniu.cn',
+    apiManager: 'http://manager.aiyangniu.cn',
+    apiCow: 'http://cow.aiyangniu.cn',
+    apiOrder: 'http://orders.aiyangniu.cn',
+    apiEcontract:'http://econtracts.aiyangniu.cn',
+    apiItem: 'https://items.aiyangniu.cn',
+  }
+}
+
+
+export default api

+ 132 - 0
src/request/modules/apiContent.js

@@ -0,0 +1,132 @@
+/**
+ * 内容服务系统
+ * apiContent:'http://10.162.16.5:8070'  //内容服务系统
+ */
+ import res from '@/utils/system/request.js'
+
+/* 获取内容列表 */
+export const GetContent = (params) => res('get', 'apiContent', '/contents/{contentTypeId}/list', {...params,params})
+
+/* 获取楼层广告数据 */
+export const GetAdvertList = (params) => res('get','apiContent', '/manager/publicitys/onLineList', {params:params})
+
+/* M003获取楼层数据 */
+export const GetFloorList = (params) => res('get','apiContent', '/manager/floors/list/'+params.status, {params:params})
+/* M011根据楼层获取楼层数据 */
+export const GetFloorListById = (params) => res('get','apiContent', '/manager/floors/'+params.id, {params:params})
+
+//根据ID获取广告详情
+export const GetAdvertDetail = (params) => res('get', 'apiContent', '/manager/publicitys/'+params.id, {params:params});
+
+//M002新增广告
+export const AddNewAdvert = (params) => res('post', 'apiContent', '/manager/publicitys/add', params);
+
+//M003更新广告信息
+export const UpdateAdvert = (params) => res('put', 'apiContent', '/manager/publicitys/update',params);
+
+//M001新增未上线的楼层广告
+export const AddFloor = (params) => res('post', 'apiContent', '/manager/floors/add', params);
+
+//M002删除未上线的楼层广告
+export const DeleteFloor = (params) => res('delete', 'apiContent', '/manager/floors/delete/'+params.id, {params});
+
+//M004楼层上线接口
+export const OnLine = (params) => res('put', 'apiContent', '/manager/floors/onLine/'+params.id,params, {form:true});
+
+//M005楼层下线接口
+export const OffLine = (params) => res('put', 'apiContent', '/manager/floors/offLine/'+params.id,params, {form:true});
+
+//M006更新楼层信息
+export const UpdateFloor = (params) => res('put', 'apiContent', '/manager/floors/update',params);
+
+//M008楼层下移接口
+export const DownMove = (params) => res('put', 'apiContent', '/manager/floors/moveDown/'+params.id,params, {form:true});
+
+//M009楼层上移接口
+export const TopMove = (params) => res('put', 'apiContent', '/manager/floors/moveUp/'+params.id,params, {form:true});
+
+//M005广告下移接口
+export const MoveDownAdvert = (params) => res('put', 'apiContent', '/manager/publicitys/moveDown/'+params.id,params, {form:true});
+
+//M005广告上移接口
+export const MoveUpAdvert = (params) => res('put', 'apiContent', '/manager/publicitys/moveUp/'+params.id,params, {form:true});
+
+//M007获取楼层数接口
+export const GetFloorNum = (params) => res('get', 'apiContent', '/manager/floors/getOnLineCount',params);
+
+//M010全部楼层接口
+export const GetFloorAll = (params) => res('get', 'apiContent', '/manager/floors/list/all',params);
+
+//Moo4广告库查询接口
+export const AdvertsList = (params) => res('get','apiContent','/manager/publicitys/library',{params});
+
+//Moo1删除楼层广告接口
+export const DeleteAdverts= (params) => res('delete','apiContent','/manager/publicitys/delete/'+params.id,{params});
+
+/**
+  * 内容管理
+  * contents : Content Controller
+  */
+/* 获取内容列表 */
+export const GetContentList = (params) => res('get','apiContent', '/manager/contents/list', {params:params})
+
+//根据ID获取内容详情
+export const GetContentDetail = (params) => res('get', 'apiContent', '/manager/contents/list/'+params.id, {params:params});
+
+//删除内容接口
+export const DeleteContent= (params) => res('delete','apiContent','/manager/contents/delete/'+params.id,{params});
+
+//删除内容类别接口
+export const DeleteConType= (params) => res('delete','apiContent','/manager/contentTypes/delete',{params});
+
+//审核内容接口
+export const AuditContent = (params) => res('put', 'apiContent', '/manager/contents/audit/'+params.id,params, {form:true});
+
+/* 获取内容类型列表 */
+export const GetContentTypeList = (params) => res('get','apiContent', '/manager/contentTypes/list', {params:params})
+
+//根据ID获取内容类型详情
+export const GetContentTypeDetail = (params) => res('get', 'apiContent', '/manager/contentTypes/list/{id}', {params:params});
+
+//新增内容
+export const AddNewContent = (params) => res('post', 'apiContent', '/manager/contents/add', params);
+
+//更新内容信息
+export const UpdateContent = (params) => res('put', 'apiContent', '/manager/contents/update',params);
+
+//新增内容类别
+export const AddNewConType = (params) => res('post', 'apiContent', '/manager/contentTypes/add', params);
+
+//更新内容类别信息
+export const UpdateConType = (params) => res('put', 'apiContent', '/manager/contentTypes/update',params);
+
+//获取APP启动页图片
+export const getBootStartImage = (params) => res('get', 'apiContent', '/bootStart/getBootStartImage', {params:params});
+//获取APP启动页图片列表
+export const getBootStartImageList = (params) => res('get', 'apiContent', '/bootStart/getBootStartImageList', {params:params});
+
+//保存APP启动页图片
+export const saveBootStartImage = (params) => res('post', 'apiContent', '/bootStart/saveBootStartImage', params);
+
+//获取文章分类列表
+export const articleClassifyList = (params) => res('get', 'apiContent', '/article/classify/list', {params:params});
+//新增文章分类
+export const addArticleClassify = (params) => res('post', 'apiContent', '/article/classify/add',params);
+//修改文章分类
+export const updateArticleClassify = (params) => res('post', 'apiContent', '/article/classify/update',params);
+//删除文章分类
+export const deleteArticleClassify = (params) => res('post', 'apiContent', '/article/classify/delete',params, {form:true});
+//获取一级文章分类
+export const getClassifyoneList = (params) => res('get', 'apiContent', '/article/classify/getClassifyoneList', {params:params});
+//获取三级文章分类
+export const getClassifyList = (params) => res('get', 'apiContent', '/article/classify/getClassifyList', {params:params});
+//文章信息列表
+export const articleList = (params) => res('get', 'apiContent', '/article/list', {params:params});
+//新增文章内容
+export const addArticle = (params) => res('post', 'apiContent', '/article/add',params);
+//更新文章信息
+export const updateArticle = (params) => res('post', 'apiContent', '/article/update',params);
+//删除文章信息
+export const deleteArticle = (params) => res('post', 'apiContent', '/article/delete',params, {form:true});
+//获取文章详情
+export const getArticleDetail = (params) => res('get', 'apiContent', '/article/list/'+params.id,{params:params});

+ 92 - 0
src/request/modules/apiCow.js

@@ -0,0 +1,92 @@
+import res from '@/utils/system/request.js'
+
+// 获取全部分类接口
+export const getCattleClassify = (params) => res('get', 'apiCow', '/manager/cattleClassify/listTree')
+// 新增牛只分类信息
+export const addCowClassify = (params) => res('post', 'apiCow', '/manager/cattleClassify/add',params)
+// 删除牛只分类信息
+export const deleteCowClassify = (params) => res('delete', 'apiCow', '/manager/cattleClassify/delete',{params:params})
+// 编辑牛只分类信息
+export const updateCowClassify = (params) => res('post', 'apiCow', '/manager/cattleClassify/update',params)
+// 新增牛只接口
+export const saveCattleItem = (params) => res('post', 'apiCow', '/manager/cattleItem/saveCattleItem',params)
+// 获取牛只重量范围列表
+export const getCattleWeightScopes = (params) => res('get', 'apiCow', '/manager/cattleItem/getCattleWeightScopes',{params:params})
+
+// 牛只商品分页查询
+export const getCattleItemPage = (params) => res('get', 'apiCow', '/manager/cattleItem/getCattleItemPage',{params:params})
+// 牛只商品下架 
+export const setOffSale = (params) => res('put', 'apiCow', '/manager/cattleItem/sku/offSale',params);
+// 牛只商品上架
+export const setToSale = (params) => res('put', 'apiCow', '/manager/cattleItem/sku/toSale',params);
+// 获取牛只详情 
+export const getCattleSpu = (params) => res('get', 'apiCow', '/manager/cattleItem/getCattleSpu',{params:params})
+// 编辑牛只商品 
+export const updateCattleSpu = (params) => res('post', 'apiCow', '/manager/cattleItem/updateCattleSpu',params)
+// 删除牛只商品 
+export const updateCattleSku = (params) => res('post', 'apiCow', '/manager/cattleItem/updateCattleSku',params)
+
+// 订单
+// 查询全部订单
+export const getCattleOrderPage = (params) => res('get', 'apiCow', '/manager/cattle/order/getCattleOrderPage',{params:params})
+
+//意向用户列表 
+export const potentialUserListPage = (params) => res('get','apiCow', '/cattle/potential/potentialUserListPage', {params:params})
+// 修改意向客户联系状态
+export const updatePotentialStatus = (params) => res('post', 'apiCow', '/cattle/potential/update', params);
+
+//查询订单详情 
+export const getCattleOrderInfo = (params) => res('get','apiCow', '/manager/cattle/order/getCattleOrderInfo', {params:params})
+// 发起改价 
+export const UpdatePrice = (params) => res('post', 'apiCow', '/manager/cattle/order/cattleOrderUpdatePrice', params);
+// 确认已看牛 
+export const confirmAppointCattle = (params) => res('post', 'apiCow', '/manager/cattle/order/confirmAppointCattle', params,{form:true});
+// 确认支付尾款 
+export const confirmLastPay = (params) => res('post', 'apiCow', '/manager/cattle/order/confirmLastPay', params,{form:true});
+
+// 采购需求
+export const getBuyDemandPage = (params) => res('get','apiCow', '/manager/buyDemand/getBuyDemandPage', {params:params})
+// 新增采购需求 
+export const saveCattleBuyDemand = (params) => res('post', 'apiCow', '/manager/buyDemand/saveCattleBuyDemand', params);
+// 展示发布和下架 
+export const offOnSale = (params) => res('put', 'apiCow', '/manager/buyDemand/offOnSale', params);
+// 获取采购需求详情
+export const getCattleBuyDemandById = (params) => res('get','apiCow', '/manager/buyDemand/getCattleBuyDemandById', {params:params})
+// 编辑采购需求 POST 
+export const updateCattleBuyDemand = (params) => res('post', 'apiCow', '/manager/buyDemand/updateCattleBuyDemand', params);
+
+// 获取发布列表 
+export const getCattleDealPage = (params) => res('get','apiCow', '/manager/cattleDeal/getCattleDealPage', {params:params})
+// 发布审核 
+export const cattleDealAudit = (params) => res('post', 'apiCow', '/manager/cattleDeal/cattleDealAudit', params,{form:true});
+// 强制下架 
+export const cattleDealoffSale = (params) => res('post', 'apiCow', '/manager/cattleDeal/offSale', params,{form:true});
+// 查询详情 
+export const getCattleDealInfo = (params) => res('get','apiCow', '/manager/cattleDeal/getCattleDealInfo', {params:params})
+
+// 增加协议
+export const addProtocol = (params)=> res('post','apiCow','/agreementinfoConfig/saveOrUpdate',params) 
+// 查询协议
+export const getProtocol = (params)=> res('post','apiCow','/agreementinfoConfig/query',params)
+// 服务费新增修改
+export const updateServiceFee = (params)=> res('post','apiCow','/cattleFeeConfig/saveOrUpdate',params)
+// 服务费查询
+export const getServiceFee = (params)=> res('post','apiCow','/cattleFeeConfig/query',params)
+
+// 后台查询  报价 、意向、订单
+export const getCattleDealPageByAdminAndType = (params) => res('get','apiCow','/manager/cattleDeal/getCattleDealPageByAdminAndType', {params});
+// 查询报价记录
+export const quoteList = params => res('post','apiCow','/manager/cattleDeal/quoteList',params)
+
+// 查询卖牛报价待审列表
+export const querySaleQuoteWaitAuditList = params => res('post','apiCow','/manager/cattleDeal/querySaleQuoteWaitAuditList',params)
+// 卖牛报价审核
+export const saleQuoteAudit = params => res('post','apiCow','/manager/cattleDeal/saleQuoteAudit',params,{form:true})
+// 查询意向单量列表
+export const intendList = params => res('post','apiCow','/manager/cattleDeal/intendList',params)
+// 查询订单列表
+export const orderList = params => res('post','apiCow','/manager/cattleDeal/orderList',params)
+// 查询子订单列表
+export const splitOrderList = params => res('post','apiCow','/manager/cattleDeal/splitOrderList',params)
+// 查询服务费列表
+export const shopFeeList = params => res('post','apiCow','/cattle/fee/shopFeeList',params)

+ 11 - 0
src/request/modules/apiEcontract.js

@@ -0,0 +1,11 @@
+/**
+ * 合同服务系统
+ */
+
+import res from '@/utils/system/request.js'
+
+// 上传服务合同
+export const uploadSignedShopFeeContract = (params) => res('post', 'apiEcontract', '/cattleContract/uploadSignedShopFeeContract',params,{form:true})
+
+// 下载买卖合同
+export const getContractDownloadUrl = (params) => res('get', 'apiEcontract', '/document/getContractDownloadUrl',{params:params})

+ 33 - 0
src/request/modules/apiItem.js

@@ -0,0 +1,33 @@
+import res from '@/utils/system/request.js'
+
+// 新增牛只参考标准
+export const createCattleDeel = (params) => res('post', 'apiItem', '/manager/cattleDeel/create', params)
+// 删除牛只参考标准
+export const deleteCattleDeel = (params) => res('post', 'apiItem', '/manager/cattleDeel/delete', params, {form:true})
+// 编辑牛只参考标准
+export const updateCattleDeel = (params) => res('post', 'apiItem', '/manager/cattleDeel/update', params)
+// 获取牛只参考标准列表
+export const cattleDeelList = (params) => res('get', 'apiItem', '/manager/cattleDeel/pageList',{params:params})
+// 维护膘情图片
+export const insertUrl = (params) => res('post', 'apiItem', '/manager/cattleDeel/insertUrl', params, {form:true})
+// 查看膘情图片
+export const getCattleFatness = (params) => res('get', 'apiItem', '/manager/cattleDeel/getCattleFatness', {params:params})
+//查询所有用户提交的计算记录(分页
+export const getCattleDealValuePage = (params) => res('get', 'apiItem', '/manager/cattleDeel/getCattleDealValuePage', {params:params})
+// 查询当前牛种信息
+export const getCattleParamOne = (params) => res('get', 'apiItem', '/manager/cattleDeel/getCattleParamOne',{params});
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 19 - 0
src/request/modules/apiOrder.js

@@ -0,0 +1,19 @@
+import res from '@/utils/system/request.js'
+
+// 审核售后订单
+export const cattleAudit = (params) => res('post', 'apiOrder', '/cattle/afterSale/audit', params)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 21 - 0
src/request/modules/apiUser.js

@@ -0,0 +1,21 @@
+import res from '@/utils/system/request.js'
+
+// 获取token
+export const GetUserToken = (params) => res('post', 'apiManager', '/manager/login', params, {form: true})
+
+// 用户信息读取接口
+export const GetUserInfo = () => res('get', 'apiManager', '/manager/managerUser',{})
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 46 - 0
src/router/createNode.js

@@ -0,0 +1,46 @@
+// 1. 用于解决keep-alive需要name的问题,动态生成随机name供keep-alive使用
+// 2. 用于解决transition动画内部结点只能为根元素的问题,单文件可写多结点
+import { defineComponent, h, createVNode, ref, nextTick } from 'vue'
+import reload from './reload.vue'
+import NProgress from '@/utils/system/nprogress'
+
+export function createNameComponent(component) {
+  return () => {
+    return new Promise((res) => {
+      component().then((comm) => {
+        const name = (comm.default.name || 'vueAdminBox') + '$' + Date.now();
+        const tempComm = defineComponent({
+          name,
+          setup() {
+            const isReload = ref(false);
+            let timeOut = null;
+            const handleReload = () => {
+              isReload.value = true;
+              timeOut && clearTimeout(timeOut);
+              NProgress.start();
+              timeOut = setTimeout(() => {
+                nextTick(() => {
+                  NProgress.done();
+                  isReload.value = false;
+                });
+              }, 260);
+            };
+            return {
+              isReload,
+              handleReload
+            };
+          },
+          render: function () {
+            if (this.isReload) {
+              return h('div', { class: 'el-main-box' }, [h(reload)]);
+            } else {
+              return h('div', { class: 'el-main-box' }, [createVNode(comm.default)]);
+            }
+          }
+        });
+        res(tempComm);
+      });
+    });
+  };
+}
+

+ 61 - 0
src/router/index.js

@@ -0,0 +1,61 @@
+/**
+ * @description 所有人可使用的参数配置列表
+ * @params hideMenu: 是否隐藏当前路由结点不在导航中展示
+ * @params alwayShow: 只有一个子路由时是否总是展示菜单,默认false
+ */
+import { createRouter, createWebHashHistory } from 'vue-router'
+import store from '@/store'
+import NProgress from '@/utils/system/nprogress'
+import { changeTitle } from '@/utils/system/title'
+
+// 动态路由相关引入数据
+import Layout from '@/layout/index.vue'
+import MenuBox from '@/components/menu/index.vue'
+import { createNameComponent } from './createNode'
+
+// 引入modules
+import Dashboard from './modules/dashboard'
+import Pages from './modules/pages'
+import System from './modules/system'
+
+let modules = [
+  ...System,
+  ...Dashboard,
+  ...Pages,
+]
+
+const routes = modules
+
+const router = createRouter({
+  history: createWebHashHistory(),
+  routes
+})
+
+const whiteList = ['/login']
+
+router.beforeEach((to, _from, next) => {
+  NProgress.start();
+  if (store.state.user.userToken || whiteList.indexOf(to.path) !== -1) {
+    to.meta.title ? (changeTitle(to.meta.title)) : ""; // 动态title
+    next()
+  } else {
+    next("/login"); // 全部重定向到登录页
+    to.meta.title ? (changeTitle(to.meta.title)) : ""; // 动态title
+  }
+});
+
+// 路由进入后出发守卫
+router.afterEach((to, _from) => {
+  const keepAliveComponentsName = store.getters['keepAlive/keepAliveComponentsName'] || []
+  const name = to.matched[to.matched.length - 1].components.default.name
+  if (to.meta && to.meta.cache && name && !keepAliveComponentsName.includes(name)) {
+    store.commit('keepAlive/addKeepAliveComponentsName', name)
+  }
+  NProgress.done();
+});
+
+export {
+  modules
+}
+
+export default router

+ 32 - 0
src/router/modules/dashboard.js

@@ -0,0 +1,32 @@
+import Layout from '@/layout/index.vue'
+import { createNameComponent } from '../createNode'
+const route = [
+  // {
+  //   path: '/',
+  //   component: Layout,
+  //   redirect: '/dashboard',
+  //   meta: { title: '', icon: 'icon-bingtutongji' },
+  //   children: [
+  //     {
+  //       path: 'dashboard',
+  //       component: createNameComponent(() => import('@/views/dashboard/index.vue')),
+  //       meta: { title: '数据看板', icon: 'icon-bingtutongji', hideClose: true }
+  //     }
+  //   ]
+  // }
+  {
+    path: '/',
+    component: Layout,
+    redirect: '/classifyManageIndex',
+    meta: { title: '', icon: 'icon-bingtutongji' },
+    children: [
+      {
+        path: 'classifyManageIndex',
+        component: createNameComponent(() => import('@/views/classifyManage/classifyManageIndex.vue')),
+        meta: { title: '牛只分类', icon: 'icon-yingyong', hideClose: true }
+      }
+    ]
+  }
+]
+
+export default route

+ 345 - 0
src/router/modules/pages.js

@@ -0,0 +1,345 @@
+import Layout from '@/layout/index.vue'
+import { createNameComponent } from '../createNode'
+const route = [
+
+  /*   {
+      path: '/beefSaleManage',
+      component: Layout,
+      redirect: 'beefSaleManage/beefOnsaleList',
+      meta: { title: '牛只上架管理', icon: 'icon--nainiu' },
+      alwayShow: true,
+      children: [
+        {
+          path: 'beefSaleManage/addNewbeef',
+          component: createNameComponent(() => import('@/views/beefSaleManage/addNewbeef.vue')),
+          meta: { title: '新上架牛源', icon: '', cache: false },
+        },
+        {
+          path: 'beefSaleManage/beefOnsaleList',
+          component: createNameComponent(() => import('@/views/beefSaleManage/beefOnsaleList.vue')),
+          meta: { title: '销售中牛源', icon: '', cache: false },
+        },
+        {
+          path: 'beefSaleManage/beefInStore',
+          component: createNameComponent(() => import('@/views/beefSaleManage/beefInStore.vue')),
+          meta: { title: '仓库中牛只', icon: '', cache: false },
+        },
+        {
+          path: 'beefSaleManage/editBeef',
+          component: createNameComponent(() => import('@/views/beefSaleManage/editBeef.vue')),
+          meta: { title: '编辑商品', hideMenuItem: true, cache: false },
+        },
+      ]
+    }, */
+  /*   {
+      path: '/requireManage',
+      component: Layout,
+      redirect: 'requireManage/procurementNeeds',
+      meta: { title: '采购需求管理', icon: 'icon-caigoudingdan' },
+      children: [
+        {
+          path: 'procurementNeeds',
+          component: createNameComponent(() => import('@/views/requireManage/procurementNeeds.vue')),
+          meta: { title: '采购需求管理', cache: false, },
+        },
+        {
+          path: 'addNeeds',
+          component: createNameComponent(() => import('@/views/requireManage/addNeeds.vue')),
+          meta: { title: '新增采购需求', hideMenuItem: true, cache: false },
+        },
+        {
+          path: 'editNeeds',
+          component: createNameComponent(() => import('@/views/requireManage/editNeeds.vue')),
+          meta: { title: '编辑采购需求', hideMenuItem: true, cache: false },
+        },
+        {
+          path: 'procurementDetail',
+          component: createNameComponent(() => import('@/views/requireManage/procurementDetail.vue')),
+          meta: { title: '采购需求详情', hideMenuItem: true, cache: false },
+        },
+      ]
+    }, */
+  {
+    path: '/userPublishSellManage',
+    component: Layout,
+    redirect: 'userPublishManage/pubSaleInfo',
+    meta: { title: '卖牛信息管理', icon: 'icon-fabu' },
+    children: [
+      {
+        path: 'pubSaleInfo/1',
+        component: createNameComponent(() => import('@/views/userPublishManage/pubSaleInfo.vue')),
+        meta: { title: '待审核', cache: false, },
+      },
+      {
+        path: 'pubSaleInfo/2',
+        component: createNameComponent(() => import('@/views/userPublishManage/pubSaleInfo.vue')),
+        meta: { title: '审核拒绝', cache: false, },
+      },
+      {
+        path: 'pubSaleInfo/3',
+        component: createNameComponent(() => import('@/views/userPublishManage/pubSaleInfo.vue')),
+        meta: { title: '发布中', cache: false, },
+      },
+      {
+        path: 'pubSaleInfo/4',
+        component: createNameComponent(() => import('@/views/userPublishManage/pubSaleInfo.vue')),
+        meta: { title: '强制下架', cache: false, },
+      },
+      {
+        path: 'pubSaleInfo/5',
+        component: createNameComponent(() => import('@/views/userPublishManage/pubSaleInfo.vue')),
+        meta: { title: '已过期', cache: false, },
+      },
+      {
+        path: 'publishDetail',
+        component: createNameComponent(() => import('@/views/userPublishManage/publishDetail.vue')),
+        meta: { title: '发布详情', hideMenuItem: true, cache: false, },
+      },
+    ]
+  },
+  {
+    path: '/userPublishBuyManage',
+    component: Layout,
+    redirect: 'userPublishManage/pubSaleInfo',
+    meta: { title: '买牛信息管理', icon: 'icon-fabu' },
+    children: [
+      {
+        path: 'pubPurchaseInfo/1',
+        component: createNameComponent(() => import('@/views/userPublishManage/pubPurchaseInfo.vue')),
+        meta: { title: '待审核', cache: false, },
+      },
+      {
+        path: 'pubPurchaseInfo/2',
+        component: createNameComponent(() => import('@/views/userPublishManage/pubPurchaseInfo.vue')),
+        meta: { title: '审核拒绝', cache: false, },
+      },
+      {
+        path: 'pubPurchaseInfo/3',
+        component: createNameComponent(() => import('@/views/userPublishManage/pubPurchaseInfo.vue')),
+        meta: { title: '发布中', cache: false, },
+      },
+      {
+        path: 'pubPurchaseInfo/4',
+        component: createNameComponent(() => import('@/views/userPublishManage/pubPurchaseInfo.vue')),
+        meta: { title: '强制下架', cache: false, },
+      },
+      {
+        path: 'pubPurchaseInfo/5',
+        component: createNameComponent(() => import('@/views/userPublishManage/pubPurchaseInfo.vue')),
+        meta: { title: '已过期', cache: false, },
+      },
+      {
+        path: 'publishDetail',
+        component: createNameComponent(() => import('@/views/userPublishManage/publishDetail.vue')),
+        meta: { title: '发布详情', hideMenuItem: true, cache: false, },
+      },
+    ]
+  },
+  {
+    path: '/quoteManage',
+    component: Layout,
+    redirect: 'quoteManage',
+    meta: { title: '报价管理', icon: 'icon-yingyong' },
+    children: [
+      {
+        path: 'buyQuote',
+        component: createNameComponent(() => import('@/views/quoteManage/buyQuote.vue')),
+        meta: { title: '买牛报价' }
+      },
+      {
+        path: 'sellQuote',
+        component: createNameComponent(() => import('@/views/quoteManage/sellQuote.vue')),
+        meta: { title: '卖牛报价' }
+      },
+      {
+        path: 'sellQuoteAudit',
+        component: createNameComponent(() => import('@/views/quoteManage/sellQuoteAudit.vue')),
+        meta: { title: '卖牛报价审核' }
+      }
+    ]
+  },
+  {
+    path: '/intentManage',
+    component: Layout,
+    redirect: 'intentManage',
+    meta: { title: '意向单管理', icon: 'icon-hezuo' },
+    children: [
+      {
+        path: 'buyIntent',
+        component: createNameComponent(() => import('@/views/intentManage/buyIntent.vue')),
+        meta: { title: '买牛意向单' }
+      },
+      {
+        path: 'sellIntent',
+        component: createNameComponent(() => import('@/views/intentManage/sellIntent.vue')),
+        meta: { title: '卖牛意向单' }
+      }
+    ]
+  },
+  {
+    path: '/orderManage',
+    component: Layout,
+    redirect: 'orderManage/orderManageIndex',
+    meta: { title: '订单管理', icon: 'icon-dingdandingdanmingxishouzhimingxi' },
+    children: [
+      // {
+      //   path: 'orderManage/orderManageIndex',
+      //   component: createNameComponent(() => import('@/views/orderManage/orderManageIndex.vue')),
+      //   meta: { title: '牛只订单管理', cache: false },
+      // },
+      {
+        path: 'orderManage/orderDetail',
+        component: createNameComponent(() => import('@/views/orderManage/orderDetail.vue')),
+        meta: { title: '牛只订单详情', hideMenuItem: true, cache: false, },
+      },
+      {
+        path: 'buyOrder',
+        component: createNameComponent(() => import('@/views/orderManage/buyOrder.vue')),
+        meta: { title: '买牛订单', cache: false, },
+      },
+      {
+        path: 'sellOrder',
+        component: createNameComponent(() => import('@/views/orderManage/sellOrder.vue')),
+        meta: { title: '卖牛订单', cache: false, },
+      },
+      {
+        path: 'contractManage',
+        component: createNameComponent(() => import('@/views/orderManage/contractManage.vue')),
+        meta: { title: '服务合同', hideMenuItem: true, cache: false, }
+      },
+    ]
+  },
+  // {
+  //   path: '/userPublishManage',
+  //   component: Layout,
+  //   redirect: 'userPublishManage/pubSaleInfo',
+  //   meta: { title: '用户发布管理', icon: 'icon-fabu' },
+  //   children: [
+  //     {
+  //       path: 'pubSaleInfo',
+  //       component: createNameComponent(() => import('@/views/userPublishManage/pubSaleInfo.vue')),
+  //       meta: { title: '卖牛信息管理', cache: false, },
+  //     },
+  //     {
+  //       path: 'pubPurchaseInfo',
+  //       component: createNameComponent(() => import('@/views/userPublishManage/pubPurchaseInfo.vue')),
+  //       meta: { title: '买牛信息管理', cache: false, },
+  //     },
+  //     {
+  //       path: 'publishDetail',
+  //       component: createNameComponent(() => import('@/views/userPublishManage/publishDetail.vue')),
+  //       meta: { title: '发布详情', hideMenuItem: true, cache: false, },
+  //     },
+  //   ]
+  // },
+  {
+    path: '/contentManage',
+    component: Layout,
+    redirect: 'contentList',
+    meta: { title: '内容管理', icon: 'icon-_guanggaochuangyi' },
+    alwayShow: true,
+    children: [
+      {
+        path: 'contentList',
+        component: createNameComponent(() => import('@/views/contentManage/contentList.vue')),
+        meta: { title: '内容列表', cache: false, },
+        children: [
+
+        ]
+      },
+      {
+        path: 'contentTypeList',
+        component: createNameComponent(() => import('@/views/contentManage/contentTypeList.vue')),
+        meta: { title: '内容类型列表', cache: false, }
+      },
+      {
+        path: 'newContent',
+        component: createNameComponent(() => import('@/views/contentManage/newContent.vue')),
+        meta: { title: '新增内容', hideMenuItem: true, cache: false, }
+      },
+      {
+        path: 'editContent',
+        component: createNameComponent(() => import('@/views/contentManage/newContent.vue')),
+        meta: { title: '编辑内容', hideMenuItem: true, cache: false, }
+      }
+    ]
+  },
+  /*   {
+      path: '/IntendedCustomers',
+      component: Layout,
+      redirect: 'IntendedCustomers',
+      meta: { title: '意向客户', icon: 'icon-hezuo' },
+      children: [
+        {
+          path: 'IntendedCustomers',
+          component: createNameComponent(() => import('@/views/IntendedCustomers/IntendedCustomers.vue')),
+          meta: { title: '意向客户', icon: 'icon-hezuo', cache: false, },
+          children: [
+  
+          ]
+        },
+      ]
+    }, */
+  {
+    path: '/protocol',
+    component: Layout,
+    redirect: 'protocol',
+    meta: { title: '协议管理', icon: 'icon-yingyong' },
+    children: [
+      {
+        path: 'protocol',
+        component: createNameComponent(() => import('@/views/protocol/index.vue')),
+        meta: { title: '协议管理',icon: 'icon-yingyong'},
+      },
+      // {
+      //   path: 'addProtocol',
+      //   component: createNameComponent(() => import('@/views/protocol/addProtocol.vue')),
+      //   meta: { title: '新增协议', hideMenuItem: true, cache: false, },
+      // }
+    ]
+
+  },
+  {
+    path: '/serviceFee',
+    component: Layout,
+    redirect: 'serviceFee',
+    meta: { title: '费用管理', icon: 'icon-yingyong' },
+    children: [
+      {
+        path: 'index',
+        component: createNameComponent(() => import('@/views/serviceFee/index.vue')),
+        meta: { title: '费用设置' },
+      },
+      {
+        path: 'shopFeeList',
+        component: createNameComponent(() => import('@/views/serviceFee/shopFeeList.vue')),
+        meta: { title: '服务费管理' },
+      }
+    ]
+  },
+  {
+    path: '/cattleTrdeValue',
+    component: Layout,
+    redirect: 'cattleTrdeValue',
+    meta: { title: '牛只交易价值测算', icon: 'icon-_guanggaochuangyi' },
+    children: [
+      {
+        path: 'cattleTrdeValue',
+        component: createNameComponent(() => import('@/views/cattleTrdeValue/index.vue')),
+        meta: { title: '牛只品种管理'},
+      },
+      {
+        path: 'referenceInfo',
+        component: createNameComponent(() => import('@/views/cattleTrdeValue/referenceInfo.vue')),
+        meta: { title: '牛只膘情参考'},
+      },
+      {
+        path: 'recondsInfo',
+        component: createNameComponent(() => import('@/views/cattleTrdeValue/recondsInfo.vue')),
+        meta: { title: '测算记录'},
+      },
+    ]
+  },
+]
+
+export default route

+ 43 - 0
src/router/modules/system.js

@@ -0,0 +1,43 @@
+import Layout from '@/layout/index.vue'
+import { createNameComponent } from '../createNode'
+const route = [
+  {
+    path: '/system',
+    component: Layout,
+    redirect: '/404',
+    hideMenu: true,
+    meta: { title: '系统目录' },
+    children: [
+      {
+        path: '/404',
+        component: createNameComponent(() => import('@/views/system/404.vue')),
+        meta: { title: '404', hideTabs: true }
+      },
+      {
+        path: '/401',
+        component: createNameComponent(() => import('@/views/system/401.vue')),
+        meta: { title: '401', hideTabs: true }
+      },
+      {
+        path: '/redirect/:path(.*)',
+        component: createNameComponent(() => import('@/views/system/redirect.vue')),
+        meta: { title: 'redirect', hideTabs: true }
+      }
+    ]
+  },
+  {
+    path: '/login',
+    component: createNameComponent(() => import('@/views/system/login.vue')),
+    hideMenu: true,
+    meta: { title: '登录', hideTabs: true }
+  },
+  {
+    // 找不到路由重定向到404页面
+    path: "/:pathMatch(.*)",
+    component: Layout,
+    redirect: "/404",
+    hideMenu: true
+  },
+]
+
+export default route

+ 116 - 0
src/router/reload.vue

@@ -0,0 +1,116 @@
+<template>
+  <div class="ok-loading">
+    <div class="ball-loader"> <span></span><span></span><span></span><span></span> </div>
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+export default defineComponent({
+  name: 'routerReload',
+  setup() {}
+});
+</script>
+
+<style lang="scss">
+.ok-loading {
+  width: calc(100% - 30px);
+  height: calc(100% - 30px);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color:  var(--system-page-background);
+  margin: 15px;
+}
+.ok-loading.close {
+  animation: close 1s;
+  -webkit-animation: close 1s;
+  animation-fill-mode: forwards;
+}
+.ball-loader {
+
+}
+.ball-loader > span,
+.signal-loader > span {
+  background-color: var(--system-primary-color);
+  display: inline-block;
+}
+
+.ball-loader > span:nth-child(1),
+.ball-loader.sm > span:nth-child(1),
+.signal-loader > span:nth-child(1),
+.signal-loader.sm > span:nth-child(1) {
+  -webkit-animation-delay: 0s;
+  animation-delay: 0s;
+}
+.ball-loader > span:nth-child(2),
+.ball-loader.sm > span:nth-child(2),
+.signal-loader > span:nth-child(2),
+.signal-loader.sm > span:nth-child(2) {
+  -webkit-animation-delay: 0.1s;
+  animation-delay: 0.1s;
+}
+.ball-loader > span:nth-child(3),
+.ball-loader.sm > span:nth-child(3),
+.signal-loader > span:nth-child(3),
+.signal-loader.sm > span:nth-child(3) {
+  -webkit-animation-delay: 0.15s;
+  animation-delay: 0.15s;
+}
+.ball-loader > span:nth-child(4),
+.ball-loader.sm > span:nth-child(4),
+.signal-loader > span:nth-child(4),
+.signal-loader.sm > span:nth-child(4) {
+  -webkit-animation-delay: 0.2s;
+  animation-delay: 0.2s;
+}
+.ball-loader > span {
+  width: 20px;
+  height: 20px;
+  margin: 0 3px;
+  border-radius: 50%;
+  transform: scale(0);
+  -ms-transform: scale(0);
+  -webkit-transform: scale(0);
+  animation: ball-load 1s ease-in-out infinite;
+  -webkit-animation: 1s ball-load ease-in-out infinite;
+}
+@-webkit-keyframes ball-load {
+  0% {
+    transform: scale(0);
+    -webkit-transform: scale(0);
+  }
+  50% {
+    transform: scale(1);
+    -webkit-transform: scale(1);
+  }
+  100% {
+    transform: scale(0);
+    -webkit-transform: scale(0);
+  }
+}
+@keyframes ball-load {
+  0% {
+    transform: scale(0);
+    -webkit-transform: scale(0);
+  }
+  50% {
+    transform: scale(1);
+    -webkit-transform: scale(1);
+  }
+  100% {
+    transform: scale(0);
+    -webkit-transform: scale(0);
+  }
+}
+@keyframes close {
+  0% {
+    opacity: 1;
+    /*display: block;*/
+  }
+  100% {
+    opacity: 0;
+    /*display: none;*/
+  }
+}
+</style>

+ 29 - 0
src/store/index.js

@@ -0,0 +1,29 @@
+import { createStore, createLogger } from 'vuex'
+import Presistent from './plugins/persistent'
+const debug = process.env.NODE_ENV !== 'production'
+
+const files = require.context("./modules/", true, /\.js$/)
+const map = {}
+for (const key of files.keys()) {
+  map[key] = files(key)
+}
+
+let modules = {}
+Object.keys(map).forEach((c) => {
+  const module = map[c].default
+  const moduleName = ('./modules/'+c.replace('./','')).replace(/^\.\/(.*)\/(.*)\.\w+$/, '$2')
+  modules[moduleName] = module
+})
+
+const presistent = Presistent({ key: 'vuex', modules, modulesKeys: {
+  local: Object.keys(modules),
+  session: []
+} })
+
+export default createStore({
+  modules: {
+    ...modules
+  },
+  strict: debug,
+  plugins: debug ? [createLogger(), presistent] : [presistent]
+})

+ 44 - 0
src/store/modules/app.js

@@ -0,0 +1,44 @@
+const state = () => ({
+  isCollapse: false, // 侧边栏是否收缩展示
+  contentFullScreen: false, // 内容是否可全屏展示
+  showLogo: true, // 是否显示Logo
+  fixedTop: false, // 是否固定顶部, todo,暂未使用
+  showTabs: true, // 是否显示导航历史
+  expandOneMenu: true, // 一次是否只能展开一个菜单
+  elementSize: 'default', // element默认尺寸,支持官网四个大小参数
+  theme: {
+    state: {
+      style: 'default',
+      primaryColor: '#409eff',
+      menuType: 'side'
+    }
+  }
+})
+
+// mutations
+const mutations = {
+  isCollapseChange(state, type) {
+    state.isCollapse = type
+  },
+  contentFullScreenChange(state, type) {
+    state.contentFullScreen = type
+  },
+  menuListChange(state, arr) {
+    state.menuList = arr
+  },
+  stateChange(state, option) {
+    state[option.name] = option.value
+  }
+}
+
+// actions
+const actions = {
+
+}
+
+export default {
+  namespaced: true,
+  state,
+  actions,
+  mutations
+}

+ 41 - 0
src/store/modules/keepAlive.js

@@ -0,0 +1,41 @@
+
+const state = () => ({
+  keepAliveComponentsName: [] // 需要缓存的组件名称
+})
+
+// mutations
+const mutations = {
+  // 重置,Push, splice keep-alive对象
+  setKeepAliveComponentsName(state, nameArr) {
+    state.keepAliveComponentsName = nameArr
+  },
+  addKeepAliveComponentsName(state, name) {
+    state.keepAliveComponentsName.push(name)
+  },
+  delKeepAliveComponentsName(state, name) {
+    const key = state.keepAliveComponentsName.indexOf(name)
+    if (key !== -1) {
+      state.keepAliveComponentsName.splice(key, 1)
+      console.log(state.keepAliveComponentsName)
+    }
+  }
+}
+
+const getters = {
+  keepAliveComponentsName(state) {
+    return state.keepAliveComponentsName
+  }
+}
+
+// actions
+const actions = {
+
+}
+
+export default {
+  namespaced: true,
+  state,
+  getters,
+  actions,
+  mutations
+}

+ 79 - 0
src/store/modules/user.js

@@ -0,0 +1,79 @@
+import * as types from '../mutation-types'
+import {user} from '@/request/api'
+
+const state = () => ({
+  token: '', // 登录token
+  userInfo: {},  // 用户信息
+})
+
+// mutations
+const mutations = {
+
+  /* 用户登录,获取token */
+  [types.USER_GET_TOKEN](state, info) {
+    if (info.code == 101) {
+      state.userToken = info.data //token
+      localStorage.aynUserToken = info.data //把token放入本地存储
+      state.isGetToken = true
+    } else {
+      state.userToken = ''
+      localStorage.aynUserToken = ''
+      state.isGetToken = false
+    }
+    state.loginMessage = info.message //登录的提示信息
+  },
+  /* 用户登录,获取用户信息 */
+  [types.USER_GET_USERINFO](state, info) {
+    if (info.code == 101) {            
+      state.userInfo = info.data || {} //用户信息
+      console.log(state.userInfo);
+      state.loginStatus = true //登录状态
+    } else {
+      state.loginMessage = info.message //登录失败提示信息
+    }
+  },
+  /* 用户退出登录 */
+  [types.USER_LOGIN_EXIT](state) {
+    state.isGetToken = false
+    state.loginStatus = false
+    state.userToken = ''
+    state.userInfo = ''
+    localStorage.aynUserToken = ''
+  },
+
+}
+
+// actions
+const actions = {
+  /* 用户登录,获取token */
+  getUserToken: async({commit}, params) => {
+    let token = await user.GetUserToken(params) //获取token
+    await commit(types.USER_GET_TOKEN, token)
+  },
+
+  /* 获取用户信息 */
+  getUserInfo: async({commit }) => {
+    let result = await user.GetUserInfo() //获取用户信息
+    await commit(types.USER_GET_USERINFO, result)
+  },
+
+   /* 退出登录 */
+  loginOut: ({
+    commit
+  }) => {
+    commit(types.USER_LOGIN_EXIT)
+  }
+}
+
+const getters = {
+  userToken: state => state.userToken,
+  userInfo: state => state.userInfo,
+}
+
+export default {
+  namespaced: true,
+  state,
+  actions,
+  getters,
+  mutations
+}

+ 9 - 0
src/store/mutation-types.js

@@ -0,0 +1,9 @@
+
+
+// 登录注册
+
+export const USER_GET_TOKEN = 'USER_GET_TOKEN'
+export const USER_GET_USERINFO = 'USER_GET_USERINFO'
+export const USER_LOGIN_EXIT = 'USER_LOGIN_EXIT'
+export const USER_GET_LOGIN_BG = 'USER_GET_LOGIN_BG'
+export const USER_GET_USERTYPE = 'USER_GET_USERTYPE'

+ 39 - 0
src/store/plugins/persistent.js

@@ -0,0 +1,39 @@
+const exclude = ['actions', 'getters', 'mutations', 'namespaced']
+export default function Presistent({ key, modules, modulesKeys }) {
+  return (store) => {
+    const localOldState = JSON.parse(localStorage.getItem(key) || '{}')
+    const sessionOldState = JSON.parse(sessionStorage.getItem(key) || '{}')
+    let oldState = {}
+    Object.assign(oldState, localOldState, sessionOldState)
+    if (Object.keys(oldState).length > 0) {
+      for (const oldKey in oldState) {
+        modules[oldKey] = oldState[oldKey]
+      }
+      store.replaceState(modules)
+    }
+    store.subscribe((mutation, state) => {
+      // 判断是否需要缓存数据至localStorage
+      if (modulesKeys.local.length > 0) {
+        const localData = setData(store.state, modulesKeys.local)
+        localStorage.setItem(key, JSON.stringify(localData))
+      } else {
+        localStorage.removeItem(key)
+      }
+      // 判断是否需要缓存数据至sessionStorage
+      if (modulesKeys.session.length > 0) {
+        const sessionData = setData(store.state, modulesKeys.session)
+        sessionStorage.setItem(key, JSON.stringify(sessionData))
+      } else {
+        sessionStorage.removeItem(key)
+      }
+    })
+  }
+}
+
+function setData(state, module) {
+  let data = {}
+  for (const i of module) {
+    data[i] = state[i]
+  }
+  return data
+}

+ 99 - 0
src/theme/index.js

@@ -0,0 +1,99 @@
+
+export const style = {
+  'default': {
+    name: '默认菜单风格',
+    menu: {
+      textColor: '#bfcbd9',
+      background: '#515a6e',
+      childrenBackground: '#1f2d3d',
+      hoverBackground: '#203448',
+      submenuActiveColor: '#fff'
+    },
+    logo: {
+      color: '#f1f1f1',
+      background: '#263445'
+    },
+    header: {
+      background: '#fff',
+      textColor: '#303133',
+      itemHoverColor: 'rgba(0,0,0,.025)',
+      breadcrumbTextColor: '#606266',
+      borderColor: '#d8dce5',
+      tabBackground: '#fff'
+    },
+    container: {
+      background: '#f8f8f8',
+      mainBackground: '#fff'
+    },
+    page: {
+      background: '#fff',
+      color: '#303133',
+      tipColor: 'rgba(0, 0, 0, 0.45)',
+      borderColor: '#ebeef5'
+    }
+  },
+  'light': {
+    name: '亮色菜单风格',
+    menu: {
+      textColor: '#272727',
+      background: '#fff',
+      childrenBackground: '#fff',
+      hoverBackground: '#f1f1f1',
+      submenuActiveColor: 'var(--system-primary-color)'
+    },
+    logo: {
+      color: '#000',
+      background: '#fff'
+    },
+    header: {
+      background: '#fff',
+      textColor: '#303133',
+      itemHoverColor: 'rgba(0,0,0,.025)',
+      breadcrumbTextColor: '#606266',
+      borderColor: '#d8dce5',
+      tabBackground: '#fff'
+    },
+    container: {
+      background: '#f8f8f8',
+      mainBackground: '#fff'
+    },
+    page: {
+      background: '#fff',
+      color: '#303133',
+      tipColor: 'rgba(0, 0, 0, 0.45)',
+      borderColor: '#ebeef5'
+    }
+  },
+  'dark': {
+    name: '暗色菜单风格',
+    menu: {
+      textColor: '#bbb',
+      background: '#18181c',
+      childrenBackground: '#18181c',
+      hoverBackground: '#000',
+      submenuActiveColor: '#fff'
+    },
+    logo: {
+      color: '#fff',
+      background: '#18181c'
+    },
+    header: {
+      background: '#18181c',
+      textColor: '#e3e3e4',
+      itemHoverColor: '#000',
+      breadcrumbTextColor: '#fff',
+      borderColor: '#3e3e3e',
+      tabBackground: '#1b1b1b'
+    },
+    container: {
+      background: '#000',
+      mainBackground: '#18181c'
+    },
+    page: {
+      background: '#18181c',
+      color: '#c7c7c7',
+      tipColor: 'rgba(255, 255, 255, 0.45)',
+      borderColor: '#3e3e3e'
+    }
+  }
+}

+ 51 - 0
src/theme/index.scss

@@ -0,0 +1,51 @@
+:root {
+  // 主题色
+  --system-primary-color: #409eff; // 可做背景色和文本色,用做背景色时,需要和--system-primary-text-color配合使用,避免文件颜色和主题色冲突
+  --system-primary-text-color: #fff; // 主题色作为背景色时使用
+
+  // logo颜色相关
+  --system-logo-color: #f1f1f1;
+  --system-logo-background: #263445;
+
+  // 菜单颜色相关
+  --system-menu-text-color: #bfcbd9;
+  --system-menu-background: #28415a;
+  --system-menu-children-background: #1f2d3d;
+  --system-menu-submenu-active-color: #fff; 
+  --system-menu-hover-background: #203448;
+
+  // header区域
+  --system-header-background: #fff;
+  --system-header-text-color: #bbb;
+  --system-header-breadcrumb-text-color: #97a8be;
+  --system-header-item-hover-color: #000;
+  --system-header-border-color: #d8dce5;
+  --system-header-tab-background: #fff;
+
+  // contaier区域,父框架
+  --system-container-background: #f8f8f8;
+  --system-container-main-background: #fff;
+
+  // 页面区域, 这一块是你在自己写的文件中使用主题,核心需要关注的地方
+  --system-page-background: #fff; // 主背景
+  --system-page-color: #303133; // 主要的文本颜色
+  --system-page-tip-color: rgba(0, 0, 0, 0.45); // 协助展示的文本颜色
+  --system-page-border-color: #000; // 通用的边框配置色,便于主题扩展
+  
+  // element主题色修改
+  --el-color-primary: var(--system-primary-color);
+}
+
+// 进度条颜色修改为主题色
+body #nprogress .bar {
+  background-color: var(--system-primary-color);
+}
+body #nprogress .peg {
+  box-shadow: 0 0 10px var(--system-primary-color), 0 0 5px var(--system-primary-color);
+}
+body #nprogress .spinner-icon {
+  border-top-color: var(--system-primary-color);
+  border-left-color: var(--system-primary-color);
+}
+
+@import './modules/dark.scss';

+ 25 - 0
src/theme/modules/dark.scss

@@ -0,0 +1,25 @@
+.dark {
+  // 通用
+  p, h1, h2, h3, h4, h5, h6, article {
+    color: var(--system-page-color);
+  }
+  .el-tree {
+    background-color: var(--system-page-background);
+    .el-tree-node__content:hover {
+      background-color: #272727;
+    }
+    & {
+      --el-color-primary-light-9: #272727;
+    }
+  }
+  .el-card {
+    background-color: var(--system-page-background);
+    color: var(--system-page-color);
+    border-color: var(--system-page-border-color);
+    .el-card__header {
+      border-color: var(--system-page-border-color);
+    }
+  }
+  // 页面内部样式修改
+  
+}

+ 134 - 0
src/utils/system/filters.js

@@ -0,0 +1,134 @@
+/**
+ * 自定义过滤器
+ */
+
+   
+
+  /* 订单状态 */
+  function orderStatus(val){
+    switch (val) {
+      // case 0:
+      //   return '待分批发货'
+      case 1:
+        return '待签署合同'
+      case 2:
+        return '待分批发货'
+    }
+  }
+
+    
+  /* 看牛模式 */
+  function previewTypes (val){
+    switch (val) {
+      case 1:
+        return '预约看牛'
+      case 2:
+        return '现场看牛'
+    }
+  }
+
+  // 支付状态
+  function payOrderState (val){
+    switch (val) {
+      case 1:
+        return '已付订金'
+      case 2:
+        return '待付订金'
+    }
+  }
+
+  // 价格单位
+  function priceUnit(val){
+    switch (val) {
+      case 1:
+        return '元/头'
+      case 2:
+        return '元/公斤'
+      case 3:
+        return '元/斤'
+    }
+  }
+
+  // 数量单位
+  function countUnit (val){
+    switch (val) {
+      case '1':
+        return '头'
+      case 2:
+        return '公斤'
+      case 3:
+        return '斤'
+    }
+  }
+
+  // 发布状态
+  function publishStatus(val){
+    switch (val) {
+      case 0:
+        return '草稿箱'
+      case 1:
+        return '待审核'
+      case 2:
+        return '已拒绝'
+      case 3:
+        return '发布中'
+      case 4:
+        return '已下架'
+      case 5:
+        return '已过期'        
+    }
+  }
+    // 报价状态
+   function quoteStatus(val) {
+      switch (val) {
+        case 0:
+          return '一轮已报价'
+        case 1:
+          return '二轮报价邀请'
+        case 2:
+          return '已失效'
+        case 3:
+          return '已形成意向单'
+        case 4:
+          return '二轮报价已报价'
+      }
+    }
+    //  意向单状态
+    function intentionStatus(val) {
+      switch (val) {
+        case 0:
+          return '待签单'
+        case 1:
+          return '签单待确认'
+        // case 2:
+        //   return '报价中'
+        case 3:
+          return '意向单作废'
+        case 4:
+          return '已生成订单'
+      }
+    }
+
+    function subOrderStatus(val) {
+      switch (val) {
+        case 1:
+          return '子订单待审核'
+        case 2:
+          return '待付款'
+        case 3:
+          return '审核拒绝'
+        case 4:
+          return '子订单修改待审核'
+        case 5:
+          return '待付款(子订单修改同意)'
+        case 6:
+          return '子订单修改拒绝'
+        case 7:
+          return '已支付'
+  
+      }
+    }
+export{
+  orderStatus,previewTypes,payOrderState,priceUnit,countUnit,publishStatus,quoteStatus,intentionStatus,subOrderStatus
+}
+

+ 12 - 0
src/utils/system/nprogress.js

@@ -0,0 +1,12 @@
+import NProgress from "nprogress"
+import "nprogress/nprogress.css"
+
+NProgress.configure({
+  easing: 'ease', // 动画方式    
+  speed: 500, // 递增进度条的速度    
+  showSpinner: true, // 是否显示加载ico    
+  trickleSpeed: 200, // 自动递增间隔    
+  minimum: 0.3 // 初始化时的最小百分比
+})
+
+export default NProgress

+ 98 - 0
src/utils/system/request.js

@@ -0,0 +1,98 @@
+import axios from 'axios'
+import store from '@/store'
+import api from '@/request/apiConfig' //真实接口配置
+import { ElMessage } from 'element-plus'
+import qs from 'qs'
+import router from '@/router/index.js'
+
+
+/**  axios基础配置 */
+axios.defaults.timeout = 50000;
+axios.defaults.headers['Content-Type'] = 'application/json;charset=UTF-8'
+
+// 请求拦截器
+axios.interceptors.request.use(
+  config => {
+    if (localStorage.aynUserToken) {
+      config.headers.common['X-AIYANGNIU-SIGNATURE'] = localStorage.aynUserToken;
+    }
+    return config;
+  },
+  err => {
+    return Promise.reject(err);
+  }
+);
+
+// 响应拦截器
+axios.interceptors.response.use(
+  response => {
+    if (response.data) {
+      let code = response.data.code
+      switch (code) {
+        case '1002': // 109 清除token信息并跳转到登录页面
+          localStorage.aynUserToken = ''
+          router.push('/login')
+          break;
+        case 110:
+          break;
+      }
+    }else{
+      showError({ code, message: response.data.code })
+    }
+    return response;
+  },
+  error => {
+    if (error.response) {
+      let status = error.response.status
+      switch (status) {
+        case 401: // 109 清除token信息并跳转到登录页面
+          localStorage.aynUserToken = ''
+          router.push('/login')
+          break;
+        case 403: // 109 清除token信息并跳转到登录页面
+          location.hash = "#/page403"
+          break;
+      }
+    }
+    let err = error.response ? (error.response.data || error.response) : (error.message || error)
+    return Promise.reject(err)
+  }
+);
+
+
+function showError(error) {
+  if (error.code === 403) {
+    // to re-login
+    store.dispatch('user/loginOut')
+  } else {
+    ElMessage({
+      message: error.msg || error.message || '服务异常',
+      type: 'error',
+      duration: 3 * 1000
+    })
+  }
+  
+}
+
+/**
+ * 二次封装axios请求
+ */
+ export default async(type, apiName, url, data, options) => {
+
+  let result = {}
+  let path = (apiName != '')? api[apiName] + url:url
+  
+  //post/put data转form
+  if(options && options.form){
+    let opt = {headers:{'Content-Type':'application/x-www-form-urlencoded'}}
+    data = qs.stringify(data)
+    options === {}?options.headers = opt.headers:options = opt
+  }
+
+  //axios
+  await axios[type](path,data,options).then((res) => {
+    result = res.data
+  })
+  
+  return result
+}

+ 39 - 0
src/utils/system/time.js

@@ -0,0 +1,39 @@
+// 用于格式化时间
+function formatDate(value, format) {
+    //value: 需要格式化的数据
+    //format: 指定格式 yyyy-MM-dd hh:mm:ss
+    let date = new Date(value);
+    // 获取年份
+    let year = date.getFullYear();
+  
+    if (/(y+)/.test(format)) {
+      // 获取匹配组的内容
+      let content = RegExp.$1;
+      format = format.replace(content, year.toString().slice(4 - content.length));
+    }
+  
+    let o = {
+      // y: date.getFullYear(),  // 用这一句也行,但只适用于四位数显示时候用
+      M: date.getMonth() + 1,
+      d: date.getDate(),
+      h: date.getHours(),
+      m: date.getMinutes(),
+      s: date.getSeconds()
+    };
+  
+    for (let key in o) {
+      // 构造动态正则
+      let reg = new RegExp(`(${key}+)`);
+  
+      if (reg.test(format)) {
+        // 获取匹配组的内容
+        let content = RegExp.$1;
+        let k = o[key] >= 10 ? o[key] : content.length == 2 ? '0' + o[key] : o[key];
+        format = format.replace(content, k);
+      }
+    }
+    return format;
+  }
+  
+  export default formatDate
+  

+ 6 - 0
src/utils/system/title.js

@@ -0,0 +1,6 @@
+// import { systemTitle } from '@/config'
+
+export function changeTitle(name) {
+  // document.title = `${name}-${systemTitle}`
+  document.title = '爱养牛-点点爱养牛-轻松养好牛'
+}

+ 32 - 0
src/utils/system/uploadCompress.js

@@ -0,0 +1,32 @@
+/*  time:20022.08.29
+**  classify:AYN Define Component
+**  author:zhangb
+**  description: 用于资源图片上传压缩,以提升终端性能,提升用户良性体验,该组件基于‘image-conversion’基础进行封装延展,视频文件不进行压缩,PNG由于无法改变分辨率,所以压缩后原型输出
+**  params: file -- 上传文件   ctimes -- 压缩比例值
+*/
+
+import * as imageConversion from 'image-conversion'; 
+
+export function compressFileM(file,ctimes) {
+  console.log('上传前的尺寸',file);
+    // 如果是视频不做压缩处理
+    if(file.type === 'video/mp4'){
+      return true
+    }
+    const isLt1M = (file.size / 1024 / 1024) * 10 < 1;
+    if (!isLt1M) {
+      let myImg = new Promise((resolve) => {
+        // 压缩值自定义,当前定义为原图的  1/<ctimes>
+        imageConversion.compressAccurately(file,{
+          size:file.size/1024/ctimes,  //指定压缩后的大小
+          type:file.type                 //指定压缩的后文件类型
+        }).then((res) => {
+          res = new File([res], file.name, { type: res.type, lastModified: Date.now() })
+          resolve(res);
+        });
+      });
+      console.log('今经过压缩处理的文件',myImg);
+      return myImg;
+    }
+  return isLt1M;
+}

+ 143 - 0
src/views/IntendedCustomers/IntendedCustomers.vue

@@ -0,0 +1,143 @@
+<template>
+  <div class="IntendedCustomers">
+    <Row>
+      <Col span="5">
+        <Input v-model="typesearchFilter.contactCell" clearable style="width:200px" placeholder="请输入手机号" @on-change="getData" />
+      </Col>
+      <Col span="9">
+        <DatePicker type="date" v-model="typesearchFilter.startTime" placeholder="请选择开始时间" style="width: 200px"  @on-change="changeStartDate" />
+        -
+        <DatePicker type="date" v-model="typesearchFilter.endTime" placeholder="请选择结束时间" style="width: 200px"  @on-change="changeEndDate" />
+      </Col>
+  </Row>
+
+    <el-table :data="tabData.data" style="width: 100%; margin-top:2em;" border>
+      <el-table-column label="序号" type="index" width="70" align="center"></el-table-column>
+      <el-table-column label="联系电话"  align="center">
+        <template #default="scope">
+          {{scope.row.contactCell}}
+        </template>
+      </el-table-column>
+      <el-table-column label="意向" align="center">
+        <template #default="scope"> 
+          <Tag color="green" v-if="scope.row.intentionType == 2">我要卖牛</Tag>
+          <Tag color="blue" v-if="scope.row.intentionType == 1">我要买牛</Tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="访问时间" align="center">
+        <template #default="scope"> 
+          {{ changeTime(scope.row.addTime) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="200"  fixed="right">
+        <template  #default="scope">
+          <el-button v-if="scope.row.contactStatus == 0" size="small" type="success" @click="changeStatus(scope.row.id)">待联系</el-button>&nbsp;
+          <el-button v-else size="small" type="info" disabled>已联系</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="page_style">
+      <Page :total="tabData.total" :model-value="currentPage" show-elevator show-total @on-change="changePage" />
+    </div>
+  </div>
+</template>
+  
+<script>
+  import { defineComponent,ref,reactive,onMounted } from 'vue'
+  import { ElMessage } from 'element-plus'
+  import { cow } from '@/request/api'
+  import moment from 'moment'
+
+  export default defineComponent({
+    components:{
+      
+    },
+    setup() {
+      
+      let typesearchFilter = reactive({limit:10,offset:0,contactCell:'',startTime:'',endTime:''})
+  
+      let tabData = ref({})
+      
+      //获取列表内容
+      async function getData(){
+        await cow.potentialUserListPage(typesearchFilter).then(res =>{
+          if (res.code == 101) {
+            tabData.value = res
+          }
+        })
+      }
+  
+      let currentPage = ref(1)
+      //更改页码
+      function changePage (page) {
+        if(tabData.value.limit){
+          typesearchFilter.offset = (page -1) *  tabData.value.limit  //更新偏移量
+          currentPage = page  //切换当前页码
+          getData()       //获取广告数据
+        }
+      }
+
+      // 改变联系状态
+      async function changeStatus(id){
+        const params = {
+          id:id,
+          contactStatus:1
+        }
+        await cow.updatePotentialStatus(params).then(res =>{
+          if (res.code == 101) {
+              ElMessage({
+                message: '操作成功!',
+                type: 'success',
+              })
+              getData()
+          }else{
+            ElMessage({
+                message: res.message || '操作失败!',
+                type: 'error',
+            })
+            getData()
+          }
+        })
+      }
+
+      // 格式化时间
+      function changeTime(val){
+       return moment(val).format('yyyy-MM-DD hh:mm:ss')
+      }
+
+      //格式化日期
+      function changeStartDate(val){
+        typesearchFilter.startTime = val
+        getData()
+      }
+      //格式化日期
+      function changeEndDate(val){
+        typesearchFilter.endTime = val
+        getData()
+      }
+
+      onMounted(()=>{
+        getData() 
+      })
+  
+      return {
+        typesearchFilter,
+        getData,
+        tabData,
+        changePage,
+        currentPage,
+        changeTime,changeStartDate,changeEndDate,changeStatus
+      }
+    },
+  })
+  </script>
+  
+  <style lang="scss" scoped>
+  .IntendedCustomers{
+    padding: 1em;
+    .page_style{
+      text-align: right; margin-top: 1em;
+      background-color: var(--system-container-background);
+    }
+  }
+  </style>

+ 420 - 0
src/views/beefSaleManage/addNewbeef.vue

@@ -0,0 +1,420 @@
+/**
+  // author:zhangb
+  // time:2023-01-09
+  // desc:新增牛只
+*/
+<template>
+  <div class="addNewbeef">
+    <div>
+     <Form ref="newCowForm" :model="newContentForm" :rules="newContentRule" :label-width="120">
+
+        <Divider orientation="left"><b>基本信息</b></Divider>
+        <Form-item label="商品名称" prop="name">
+          <Input v-model="newContentForm.name" placeholder="请输入内容名称" style="width: 480px"></Input>
+        </Form-item>
+        <Form-item label="所属分类" prop="classifyId">
+          <Cascader :data="classifyDataList" v-model="newContentForm.classifyId" @on-change="changeSelClassify" style="width:30%;" />
+        </Form-item>
+        <Form-item label="牛只所在地" prop="blongArea">
+          <Cascader :data="areadata" change-on-select v-model="newContentForm.blongArea"  @on-change="changeSelArea" style="width:30%" />
+        </Form-item>
+        <Form-item label="详细地址" prop="addressInfo">
+          <Input v-model="newContentForm.addressInfo" type="textarea" placeholder="请输入链接地址" style="width: 480px"></Input>
+        </Form-item>
+
+        <Divider orientation="left" style="margin-top:2em;"><b>牛只规格</b></Divider>
+        <Form-item label="牛只重量">
+          <Select :label-in-value="true" v-model="selectCowWeight" multiple style="width:45%" @on-change="changeSelWeight" placeholder="请选择牛只规格重量(多选)">
+            <Option v-for="item in cowWeightList" :value="item" :key="item" >{{ item }}</Option>
+          </Select>
+          <Button type="success" style="margin-left:3em" @click="confirmCreateVersion">确定生成规格明细</Button>
+        </Form-item>
+        
+        <!-- 表格生成规格 -->
+        <div style="width: 90%; margin:0 auto; margin-bottom:2em;">
+          <div style="font-size:13px; margin:1em 0;"> <Tag color="red">当前规格</Tag></div>
+          <el-table :data="cattleSkuList" >
+            <el-table-column type="index" label="序号" width="65" align="center" />
+            <el-table-column prop="weight" label="规格重量"  width="140" />
+            <el-table-column label="牛只数量" max-width="200">
+              <template #default="scope">
+                <Input v-model="scope.row.count" placeholder="库存数量" style="width: 90%"></Input>
+              </template>
+            </el-table-column>
+            <el-table-column label="牛只价格" max-width="200">
+              <template #default="scope">
+                <Input v-model="scope.row.price" placeholder="牛只价格" style="width: 90%"></Input>
+              </template>
+            </el-table-column>
+            <el-table-column label="单位" max-width="200">
+              <template #default="scope">
+                <Input v-model="scope.row.unit" placeholder="单位" style="width: 90%"></Input>
+              </template>
+            </el-table-column>
+            <el-table-column label="起售量" max-width="200">
+              <template #default="scope">
+                <Input v-model="scope.row.quantity" placeholder="起售量" style="width: 90%"></Input>
+              </template>
+            </el-table-column>
+            <el-table-column prop="address" label="规格图片" >
+              <template #default="scope">
+                <Upload
+                  ref=""
+                  type="drag"
+                  :action="uploadUrl"
+                  :headers="{'X-AIYANGNIU-SIGNATURE':token}"
+                  :show-upload-list="false"
+                  :on-success="verHandleSuccess"
+                  :before-upload="(e)=>verBeforeUpload(e,scope.$index)"
+                  :format="['jpg','jpeg','png']"
+                  :max-size="5120 * 1024 * 1024"
+                  :on-format-error="handleFormatError"
+                  class="upload_version_img">
+                  <div v-if="!scope.row.imgUrl" class="upload_border">
+                    <span>+</span>
+                  </div>
+                  <div v-else style="width:100px;height:100px;"> <img :src="scope.row.imgUrl" style="width:100%; height:100%;" alt=""></div>
+                </Upload>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        
+        <Form-item label="看牛模式" prop="previewType">
+          <Select v-model="newContentForm.previewType" :label-in-value="true" placeholder="请选择看牛模式" style="width:30%">
+            <Option v-for="type in lookCowType" :key="type.typeVal" :value="type.typeVal">{{type.typeLabel}}</Option>
+          </Select>
+        </Form-item>
+
+        <Form-item label="看牛定金" prop="previewMoney">
+          <InputNumber v-model="newContentForm.previewMoney" placeholder="请输入看牛定金"  style="width: 15%" />&nbsp;&nbsp;&nbsp;元 / 头
+        </Form-item>
+
+        <Form-item label="牛只展示图" prop="mainImgData">
+          <div class="demo-upload-list" v-for="item in mainUploadCowImg" :key="item">
+            <template v-if="item.status === 'finished'">
+              <Image :src="item.url" fit="cover" width="100%" height="100%" />
+              <div class="demo-upload-list-cover">
+                <Icon type="ios-eye-outline" @click="handleView(item.url)"></Icon>
+                <Icon type="ios-trash-outline" @click="handleRemove(item)"></Icon>
+              </div>
+            </template>
+            <template v-else>
+              <Progress v-if="item.showProgress" :percent="item.percentage" hide-info></Progress>
+            </template>
+          </div>
+          <Upload
+            ref="mainImgUpload"
+            :headers="{'X-AIYANGNIU-SIGNATURE':token}"
+            :show-upload-list="false"
+            :on-success="handleSuccess"
+            :on-format-error="handleFormatError"
+            :before-upload="beforeUpload"
+            multiple
+            type="drag"
+            :action="uploadUrl"
+            style="display: inline-block;width:58px;">
+            <div style="width: 58px;height:58px;line-height: 58px;">
+              <Icon type="ios-camera" size="20"></Icon>
+            </div>
+          </Upload>
+          <ImagePreview v-model="preViewImgModel" :preview-list="preViewImg" />
+          <div style="color:#aaa;font-size:12px;">图片格式为 jpg、jpeg、png</div>
+        </Form-item>
+       
+        <Form-item label="牛只详情" prop="content">
+          <BasicEditor ref="editotTool"></BasicEditor>
+        </Form-item>
+      </Form>
+    </div>
+    <div style="text-align:center;margin-bottom:3em;">
+      <Button @click="" style="margin-right:3em;">取消返回</Button>
+      <Button type="warning" @click="confirmSubAddNewCow(1)" style="margin-right:3em;">保存在仓库中</Button>
+      <Button type="primary" @click="confirmSubAddNewCow(3)">直接上架牛源</Button>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Message,Modal,Spin } from 'view-ui-plus'
+import { defineComponent, reactive,getCurrentInstance,onMounted,ref} from 'vue'
+import BasicEditor from '@/components/wangEditor/BasicEditor.vue'
+import {compressFileM} from '@/utils/system/uploadCompress.js'
+import areadata from './assets/area.json'
+import api from '@/request/apiConfig' //真实接口配置
+import router from '@/router'
+import {cow} from '@/request/api'
+
+export default defineComponent({
+  components:{
+    BasicEditor
+  },
+  setup() {
+    let {proxy} = getCurrentInstance();
+    const token = localStorage.aynUserToken
+    let newContentForm = reactive({
+      classifyId:[] ,blongArea: [], addressInfo:'',previewType:'',previewMoney:null,name:'',mainImgData: [],content:'',classifyOne:'',
+      classifyOneName:'',classifyTwo:'',classifyTwoName:'',classifyThree:'',classifyThreeName:'',province:'',city:'',area:'',picUrl:'',
+    })
+    // 表单验证项
+    let newContentRule = ref({
+      classifyId: [{ required: true, type:'array', message: '请选择所属类别', trigger: 'change' }],
+      blongArea: [{ required: true, type:'array', message: '请选择牛只所在地', trigger: 'change' }],
+      addressInfo:[{ required: true, message: '请填写详细地址', trigger: 'blur' }],
+      previewType:[{ required: true, type:'number', message: '请选择看牛模式', trigger: 'change' }],
+      previewMoney:[
+        { required: true, message: '请输入看牛订金',},
+        { message: '小数保留2位', trigger:'change', pattern:/^(([1-9]\d{0,3})|0)(\.\d{0,2})?$/,}
+      ],
+      name: [{ required: true, message: '请填写内容名称', trigger: 'blur' }],
+      isShow: [{ required: true, message: '请选择展示状态', trigger: 'change' }],
+      mainImgData:[{ required: true, type:'array', message: '请上传牛只主图', trigger: 'change' }],
+    })
+    const uploadUrl = api.apiManager + '/manager/images/upload'
+    let lookCowType = ref([
+      {typeVal:1,typeLabel:'预约看牛:先预约,后到现场看牛'},
+      {typeVal:2,typeLabel:'现场看牛:现场看牛,无需预约'},
+    ])
+    
+
+    // 获取全部牛只分类内容
+    let classifyDataList = ref([])  //分类列表数据
+    async function getAllClassifyData(){
+      await cow.getCattleClassify().then(res=>{
+        if(res.code === 101){
+          let finalData = rinseClassiftData(res.data)
+          console.log('结果数据',finalData);
+          classifyDataList.value = finalData
+        }
+      })
+    }
+
+    // 清洗分类数据
+    function rinseClassiftData(data){
+      data = JSON.parse(JSON.stringify(data).replace(/name/g, 'label'))
+      data = JSON.parse(JSON.stringify(data).replace(/id/g, 'value'))
+      return data
+    }
+    
+    let cowWeightList = ref([])
+    // 获取牛只体重列表
+    function getCowWeightSelList(){
+      cow.getCattleWeightScopes().then(res=>{
+        if(res.code === 101){
+          cowWeightList.value = res.data || []
+        }
+      })
+    }
+
+    // 改变选择的分类
+    function changeSelClassify(val,arr){
+      newContentForm.classifyOne = arr[0]?arr[0].value:''
+      newContentForm.classifyOneName = arr[0]?arr[0].label:''
+      newContentForm.classifyTwo = arr[1]?arr[1].value:''
+      newContentForm.classifyTwoName = arr[1]?arr[1].label:''
+      newContentForm.classifyThree = arr[2]?arr[2].value:''
+      newContentForm.classifyThreeName = arr[2]?arr[2].label:''
+    }
+
+    function changeSelArea(val,arr){
+      newContentForm.province = arr[0]?arr[0].label:''
+      newContentForm.city = arr[1]?arr[1].label:''
+      newContentForm.area = arr[2]?arr[2].label:''
+    }
+
+    function beforeUpload(file){
+      // 进行2.5倍系数压缩
+      return (compressFileM(file,2.5))
+    }
+
+    let mainUploadCowImg = ref([])  //主图列表
+    function handleSuccess(res, file, fileList){
+      if(res.code === 101){
+        file.url = res.data
+        newContentForm.mainImgData = mainUploadCowImg.value
+      }
+    }
+
+    let optVersionIndex = ref('')  //记录操作规格下标
+
+    let selectCowWeight = ref([]) //多选牛只数据
+    let cattleSkuList = ref([]) //牛只规格数据
+
+    // 规格单独上传内容
+    function verBeforeUpload(file,ind){
+      console.log('返回内容',file,ind);
+      optVersionIndex = ind
+    }
+    
+    function verHandleSuccess(res, file, fileList){
+      if(res.code == '101'){
+        cattleSkuList.value[optVersionIndex].imgUrl = res.data
+      }
+    }
+
+    function handleFormatError (file) {
+      Message.error({content: '文件格式不正确'})
+    }
+    
+    // 选择牛只规格
+    function changeSelWeight(val){
+      console.log('牛只规格',val);
+      selectCowWeight = val
+    }
+    // 生成规格明细
+    function confirmCreateVersion(){
+      cattleSkuList.value = []
+      selectCowWeight.forEach(item => {
+        let newobj = {}
+        newobj.weight = item.label
+        newobj.selWifghtVal = item.value
+        newobj.count = null
+        newobj.price = null
+        newobj.unit = null
+        newobj.quantity = null
+        newobj.imgUrl = ''
+        cattleSkuList.value.push(newobj)
+      })
+    }
+
+    // 预览上传主图
+    let preViewImg = ref([])
+    let preViewImgModel = ref(false)
+    function handleView (name) {
+      preViewImg.value = [name];
+      preViewImgModel.value = true;
+    }
+
+    function handleRemove (file) {
+      const fileList = proxy.$refs.mainImgUpload.fileList;
+      proxy.$refs.mainImgUpload.fileList.splice(fileList.indexOf(file), 1);
+    }
+
+    // 上架或保存仓库
+    function confirmSubAddNewCow(type){
+      newContentForm.content = proxy.$refs.editotTool.valueHtml  //富文本传值
+      proxy.$refs['newCowForm'].validate(async (valid) => {
+        if (valid) {
+          let sendParams = JSON.parse(JSON.stringify(newContentForm))
+          sendParams.picUrl = proxy.$refs.mainImgUpload.fileList.map(list=>{return list.url}).join('#')
+          sendParams.cattleSkuList = cattleSkuList.value
+          sendParams.skuStatus = type
+          console.log('最终组合的参数',sendParams);
+          let tipFont = ''
+          type == 1? tipFont = '保存至仓库中': tipFont ='直接上架销售'
+          Modal.confirm({
+              title: '温馨提示',
+              content: `<p>您确定将当前牛只信息<b> ${tipFont} </b>?</p>`,
+              onOk: () => {
+                subreq()
+              },
+              onCancel: () => {
+                Message.info('您已取消操作');
+              }
+          });
+          // 提交请求
+          async function subreq(){
+            Spin.show()
+            await cow.saveCattleItem(sendParams).then(res=>{
+              Spin.hide()
+              if(res.code == 101){
+                Message.success({content: res.message || '操作成功'})
+                type == 1?router.push('beefInStore'):router.push('beefOnsaleList')
+              }else{
+                Message.error({content: res.message || '操作失败'})
+              }
+            }).catch(e=>{
+              Spin.hide()
+              Message.error({content: e.message || '操作失败'})
+            })
+          }
+          
+        } else {
+          Message.warning({content:'请填写完表单数据'})
+        }
+      })
+    }
+
+    onMounted(async()=>{
+      mainUploadCowImg.value = proxy.$refs.mainImgUpload.fileList
+      getAllClassifyData()  //获取全部分类
+      getCowWeightSelList() //获取牛只选择体重列表
+    })
+
+    return {
+      uploadUrl,newContentRule,newContentForm,token,classifyDataList,areadata,selectCowWeight,cowWeightList,cattleSkuList,lookCowType,preViewImg,preViewImgModel,mainUploadCowImg,
+      beforeUpload,handleFormatError,handleSuccess,confirmCreateVersion,changeSelWeight,verBeforeUpload,verHandleSuccess,handleView,handleRemove,changeSelClassify,
+      changeSelArea,confirmSubAddNewCow,
+    }
+  },
+  methods: {
+    
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.addNewbeef{
+  padding: 1em;
+  .upload_outer_img{
+    max-width: 45%;
+    img{
+      width: 100%; padding: 0;
+    }
+    .upload_border{
+      width: 100%; padding: 3em;
+    }
+  }
+  .upload_version_img{
+    width: 100px; height: 100px;
+    border-radius: 2px; overflow: hidden;
+    .upload_border{
+      line-height: 100px; font-size: 35px; font-weight: bold; color:#ccc;
+      background-color: #f9f9f9;
+    }
+  }
+  .demo-upload-list{
+    display: inline-block;
+    width: 60px;
+    height: 60px;
+    text-align: center;
+    line-height: 60px;
+    border: 1px solid transparent;
+    border-radius: 4px;
+    overflow: hidden;
+    background: #fff;
+    position: relative;
+    box-shadow: 0 1px 1px rgba(0,0,0,.2);
+    margin-right: 4px;
+  }
+  .demo-upload-list img{
+    width: 100%;
+    height: 100%;
+  }
+  .demo-upload-list-cover{
+    display: none;
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    background: rgba(0,0,0,.6);
+  }
+  .demo-upload-list:hover .demo-upload-list-cover{
+    display: block;
+  }
+  .demo-upload-list-cover i{
+    color: #fff;
+    font-size: 20px;
+    cursor: pointer;
+    margin: 0 2px;
+  }
+}
+</style>
+<style lang="scss">
+.addNewbeef{
+  .ivu-upload-drag{
+    border: 0px;
+  }
+}
+</style>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 11535 - 0
src/views/beefSaleManage/assets/area.json


+ 260 - 0
src/views/beefSaleManage/beefInStore.vue

@@ -0,0 +1,260 @@
+/**
+  // author:zhangb
+  // time:2023-01-09
+  // desc:仓库中牛只
+*/
+<template>
+  <div class="beefInStore">
+     <!-- 筛选 -->
+     <Row :gutter="8">
+      <Col span="5">
+        <Input placeholder="请输入商品名称" v-model="filtInfoData.name" @on-change="getData" style="width: 95%" clearable>
+        </Input>
+      </Col>
+      <Col span="4">
+        <Cascader :data="classifyDataList" change-on-select v-model="filtInfoData.classifyId" @on-change="changeSelClassify" style="width:13rem;" />
+      </Col>
+    </Row>
+    <!-- 表格部分 -->
+    <el-table :data="onStoreTabData.data" border style="width: 100%; margin-top:20px;">
+      <el-table-column type="expand">
+          <template #default="props">
+            <div style="width:90%; margin:1em auto;">
+              <el-table :data="props.row.cattleSkuList">
+                <el-table-column type="index" label="序号" width="55" align="center" />
+                <el-table-column label="规格重量" prop="weight" />
+                <el-table-column label="牛只价格" prop="price" >
+                  <template #default="scope">
+                    <Numeral :value="scope.row.price" format="0,0.00" style="color:#D64E47; font-weight:bold;">
+                      <template #prefix>
+                        <strong>¥</strong>
+                      </template>
+                    </Numeral>
+                  </template>
+                </el-table-column>
+                <el-table-column label="单位" prop="unit" />
+                <el-table-column label="牛只数量(头)" prop="count" />
+                <el-table-column label="起售量(头)" prop="quantity" />
+                <el-table-column label="规格图片"  >
+                  <template #default="scope">
+                    <img :src="scope.row.imgUrl"  style="width: 5rem"/>
+                  </template>
+                </el-table-column>
+                <el-table-column label="操作" width="200"  fixed="right">
+                  <template  #default="scope">
+                    <el-button  size="small" type="success" @click="toSafe(scope.row.id)">上架</el-button>&nbsp;
+                    <el-button  size="small" type="danger" @click="delBeef(true,scope.row.id)">删除</el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+          </template>
+      </el-table-column>
+      <el-table-column type="index" label="序号" width="55" align="center" />
+      <el-table-column prop="name" label="商品名称"/>
+      <el-table-column label="上架时间" align="center" width="170">
+        <template #default="scope">
+          {{ moment(scope.row.addTime).format('yyyy-MM-DD hh:mm:ss')}}
+        </template>
+      </el-table-column>
+      <el-table-column label="所属分类" width="200"  align="center">
+        <template #default="scope">
+          {{ scope.row.classifyOneName}}/{{scope.row.classifyTwoName }}/{{scope.row.classifyThreeName  }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="previewMoney" label="看牛定金(元/头)"  align="center" >
+        <template #default="scope">
+          <Numeral :value="scope.row.previewMoney" format="0,0.00" style="color:#D64E47; font-weight:bold;">
+            <template #prefix>
+              <strong>¥</strong>
+            </template>
+          </Numeral>
+        </template>
+      </el-table-column>
+      <el-table-column prop="previewType" label="看牛模式" align="center">
+        <template #default="scope">
+          <Tag color="green" v-if="scope.row.previewType == 2">现场看牛</Tag>
+          <Tag color="magenta" v-if="scope.row.previewType == 1">预约看牛</Tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="牛只所在地" width="250" >
+        <template #default="scope">
+          <Icon type="md-pin" color="#2db7f5" /> 
+          <span style="color:#999; font-size:12px;">{{ scope.row.province}}-{{ scope.row.city  }}-{{scope.row.area }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="150"  fixed="right">
+        <template  #default="scope">
+          <el-button  size="small" type="primary" @click="editBeef(scope.row.id)">编辑</el-button>&nbsp;
+          <el-button  size="small" type="danger" @click="delBeef(false,scope.row.id)">删除</el-button>&nbsp;
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <div class="page_style">
+      <Page :total="onStoreTabData.total" :model-value="currentPage" show-elevator show-total @on-change="changePage" />
+    </div>
+    <!-- 确定框 -->
+    <Modal v-model="showModal" title="提示" @on-ok="ok" @on-cancel="cancel">
+        <p v-if="delStatus">确定要上架此商品吗?</p>
+        <p v-if="!delStatus">确定要删除此商品吗?</p>
+    </Modal>
+  </div>
+</template>
+
+<script>
+import { defineComponent, ref ,reactive, onMounted} from 'vue'
+import { cow } from '@/request/api'
+import moment from 'moment'
+
+export default defineComponent({
+  components:{
+    
+  },
+  setup() {
+   
+   let filtInfoData = reactive({ // 搜索
+      limit:10,offset:0,
+      name:'',
+      classifyId:'',
+      status:'1'
+    })
+
+    let onStoreTabData = ref([])  // 列表数据
+    let delStatus = ref(true)  // 区分删除和上架
+    let del = ref(true)  // 区分删除的数据
+    let delId = ref()  // 区分删除的数据
+
+    //获取列表内容
+    async function getData(){
+      await cow.getCattleItemPage(filtInfoData).then(res =>{
+        if (res.code == 101) {
+          onStoreTabData.value = res
+        }
+      })
+    }
+
+    // 获取全部牛只分类内容
+    let classifyDataList = ref([])  //分类列表数据
+    async function getAllClassifyData(){
+      await cow.getCattleClassify().then(res=>{
+        if(res.code === 101){
+          let finalData = rinseClassiftData(res.data)
+          classifyDataList.value = finalData
+        }
+      })
+    }
+    // 清洗分类数据
+    function rinseClassiftData(data){
+      data = JSON.parse(JSON.stringify(data).replace(/name/g, 'label'))
+      data = JSON.parse(JSON.stringify(data).replace(/id/g, 'value'))
+      return data
+    }
+    
+    // 改变选择的分类
+    function changeSelClassify(val,arr){
+      filtInfoData.classifyOne = arr[0]?arr[0].value:''
+      filtInfoData.classifyTwo = arr[1]?arr[1].value:''
+      filtInfoData.classifyThree = arr[2]?arr[2].value:''
+      getData()
+    }
+
+    let currentPage = ref(1)
+    //更改页码
+    function changePage (page) {
+      if(onStoreTabData.value.limit){
+        filtInfoData.offset = (page -1) *  onStoreTabData.value.limit  //更新偏移量
+        currentPage = page  //切换当前页码
+        getData() 
+      }
+    }
+
+    const showModal = ref(false)
+    const skuIds = ref([])
+
+    onMounted(()=>{
+      getData() 
+      getAllClassifyData()
+    })
+
+    return {
+      onStoreTabData,filtInfoData,currentPage,classifyDataList,showModal,skuIds,delStatus,del,delId,getAllClassifyData,changeSelClassify,
+      getData,changePage,moment
+    }
+  },
+  methods: {
+      //牛只上架
+      toSafe(id){
+        this.skuIds = []
+        this.skuIds.push(id)
+        this.delStatus = true
+        this.showModal = true
+      },
+      // 确定上架
+      async ok () {
+        this.showModal = false
+        if(this.delStatus){
+          // 上架
+          await cow.setToSale(this.skuIds).then(res =>{
+            if (res.code == 101) {
+              this.$message.success(res.message)
+              this.getData()
+            }else{
+              this.$message.error(res.message)
+            }
+          })
+        }else{
+          const cattleSpu ={
+            id:this.delId,
+            deleted:"1"
+          }
+          if(this.del){
+            // 删除牛只
+            await cow.updateCattleSku(cattleSpu).then(res =>{
+              if (res.code == 101) {
+                this.$message.success('删除成功')
+                this.getData()
+              }else{
+                this.$message.error(res.message)
+              }
+            })
+          }else{
+            // 删除商品
+            await cow.updateCattleSpu(cattleSpu).then(res =>{
+              if (res.code == 101) {
+                this.$message.success('删除成功')
+                this.getData()
+              }else{
+                this.$message.error(res.message)
+              }
+            })
+          }
+        }
+      },
+      cancel () {
+        this.showModal = false
+      },
+      // 编辑
+      editBeef(id){
+        this.$router.push('editBeef?id='+id)
+      },
+      // 删除
+      async delBeef(val,id){
+        this.delStatus = false
+        this.showModal = true
+        this.del = val  
+        this.delId = id
+      }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.beefInStore{
+  padding: 1em;
+  .page_style{
+    text-align: right; margin-top: 1em;
+    background-color: var(--system-container-background);
+  }
+}
+</style>

+ 214 - 0
src/views/beefSaleManage/beefOnsaleList.vue

@@ -0,0 +1,214 @@
+/**
+  // author:zhangb
+  // time:2023-01-09
+  // desc:销售中牛只
+*/
+<template>
+  <div class="beefOnsaleList">
+     <!-- 筛选 -->
+     <Row :gutter="8">
+      <Col span="5">
+        <Input placeholder="请输入商品名称" v-model="filtInfoData.name" @on-change="getData" style="width: 95%" clearable>
+        </Input>
+      </Col>
+      <Col span="4">
+        <Cascader :data="classifyDataList" change-on-select v-model="filtInfoData.classifyId" @on-change="changeSelClassify" style="width:13rem;" />
+      </Col>
+    </Row>
+    <!-- 表格部分 -->
+    <el-table :data="onsaleTabData.data" border style="width: 100%; margin-top:20px;">
+      <el-table-column type="expand">
+          <template #default="props">
+            <div style="width:90%; margin:1em auto;">
+              <el-table :data="props.row.cattleSkuList" size="small">
+                <el-table-column type="index" label="序号" width="55" align="center" />
+                <el-table-column label="规格重量" prop="weight" />
+                <el-table-column label="牛只价格" >
+                  <template #default="scope">
+                    <Numeral :value="scope.row.price" format="0,0.00" style="color:#D64E47; font-weight:bold;">
+                      <template #prefix>
+                        <strong>¥</strong>
+                      </template>
+                    </Numeral>
+                  </template>
+                </el-table-column>
+                <el-table-column label="单位" prop="unit" />
+                <el-table-column label="库存数量(头)" prop="count" />
+                <el-table-column label="起售量(头)" prop="quantity" />
+                <el-table-column label="规格图片"  >
+                  <template #default="scope">
+                    <img :src="scope.row.imgUrl"  style="width: 5rem;"/>
+                  </template>
+                </el-table-column>
+                <el-table-column label="操作" width="200"  fixed="right">
+                  <template  #default="scope">
+                    <el-button  size="small" type="danger" @click="offSafe(scope.row.id)">下架</el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+          </template>
+      </el-table-column>
+      <el-table-column type="index" label="序号" width="55" align="center" />
+      <el-table-column prop="name" label="商品名称" />
+      <el-table-column label="上架时间" align="center" width="170">
+        <template #default="scope">
+          {{ moment(scope.row.addTime).format('yyyy-MM-DD hh:mm:ss')}}
+        </template>
+      </el-table-column>
+      <el-table-column label="所属分类" align="center" width="200">
+        <template #default="scope">
+          {{ scope.row.classifyOneName}}/{{scope.row.classifyTwoName }}/{{scope.row.classifyThreeName  }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="previewMoney" label="看牛定金(元/头)" align="center" width="130">
+        <template #default="scope">
+          <Numeral :value="scope.row.previewMoney" format="0,0.00" style="color:#D64E47; font-weight:bold;">
+            <template #prefix>
+              <strong>¥</strong>
+            </template>
+          </Numeral>
+        </template>
+      </el-table-column>
+      <el-table-column label="看牛模式" align="center" width="100">
+        <template #default="scope">
+          <Tag color="green" v-if="scope.row.previewType == 2">现场看牛</Tag>
+          <Tag color="magenta" v-if="scope.row.previewType == 1">预约看牛</Tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="牛只所在地">
+        <template #default="scope">
+          <Icon type="md-pin" color="#2db7f5" /> 
+          <span style="color:#999; font-size:12px;">{{ scope.row.province}}-{{ scope.row.city  }}-{{scope.row.area }}</span>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column label="操作" width="200"  fixed="right">
+        <template  #default="scope">
+          <el-button  size="small" type="danger" @click="offSafe(scope.row.id)">下架</el-button>
+        </template>
+      </el-table-column> -->
+    </el-table>
+    <!-- 分页 -->
+    <div class="page_style">
+      <Page :total="onsaleTabData.total" :model-value="currentPage" show-elevator show-total @on-change="changePage" />
+    </div>
+    <!-- 确定框 -->
+    <Modal v-model="showModal" title="提示" @on-ok="ok" @on-cancel="cancel">
+        <p>确定要下架此商品吗?</p>
+    </Modal>
+  </div>
+</template>
+
+<script>
+import { defineComponent, ref ,reactive, onMounted} from 'vue'
+import { cow } from '@/request/api'
+import moment from 'moment'
+
+export default defineComponent({
+  components:{
+    
+  },
+  setup() {
+   
+   let filtInfoData = reactive({ // 搜索
+      limit:10,offset:0,
+      name:'',
+      classifyId:[],
+      status:'3'
+    })
+
+    let onsaleTabData = ref([])  // 列表数据
+
+    //获取列表内容
+    async function getData(){
+      await cow.getCattleItemPage(filtInfoData).then(res =>{
+        if (res.code == 101) {
+          onsaleTabData.value = res
+        }
+      })
+    }
+
+    // 获取全部牛只分类内容
+    let classifyDataList = ref([])  //分类列表数据
+    async function getAllClassifyData(){
+      await cow.getCattleClassify().then(res=>{
+        if(res.code === 101){
+          let finalData = rinseClassiftData(res.data)
+          classifyDataList.value = finalData
+        }
+      })
+    }
+    // 清洗分类数据
+    function rinseClassiftData(data){
+      data = JSON.parse(JSON.stringify(data).replace(/name/g, 'label'))
+      data = JSON.parse(JSON.stringify(data).replace(/id/g, 'value'))
+      return data
+    }
+    
+    // 改变选择的分类
+    function changeSelClassify(val,arr){
+      filtInfoData.classifyOne = arr[0]?arr[0].value:''
+      filtInfoData.classifyTwo = arr[1]?arr[1].value:''
+      filtInfoData.classifyThree = arr[2]?arr[2].value:''
+      getData()
+    }
+
+    let currentPage = ref(1)
+    //更改页码
+    function changePage (page) {
+      if(onsaleTabData.value.limit){
+        filtInfoData.offset = (page -1) *  onsaleTabData.value.limit  //更新偏移量
+        currentPage = page  //切换当前页码
+        getData() 
+      }
+    }
+
+    const showModal = ref(false)
+    const skuIds = ref([])
+
+    onMounted(()=>{
+      getData() 
+      getAllClassifyData()  //获取全部分类
+
+    })
+
+    return {
+      onsaleTabData,filtInfoData,currentPage,classifyDataList,showModal,skuIds,getAllClassifyData,
+      getData,changePage,changeSelClassify,moment
+    }
+  },
+  methods: {
+      //牛只下架
+      offSafe(id){
+        this.skuIds = []
+        this.skuIds.push(id)
+        this.showModal = true
+      },
+      //确定下架
+      async ok () {
+        this.showModal = false
+        await cow.setOffSale(this.skuIds).then(res =>{
+        if (res.code == 101) {
+          this.$message.success(res.message)
+          this.getData()
+        }else{
+          this.$message.error(res.message)
+        }
+      })
+      },
+      cancel () {
+        this.showModal = false
+      }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.beefOnsaleList{
+  padding: 1em;
+  .page_style{
+    text-align: right; margin-top: 1em;
+    background-color: var(--system-container-background);
+  }
+}
+</style>

+ 460 - 0
src/views/beefSaleManage/editBeef.vue

@@ -0,0 +1,460 @@
+/**
+  // author:zhangb
+  // time:2023-01-09
+  // desc:编辑牛只
+*/
+<template>
+  <div class="editBeef">
+    <div>
+     <Form ref="newCowForm" :model="newContentForm" :rules="newContentRule" :label-width="120">
+
+        <Divider orientation="left"><b>基本信息</b></Divider>
+        <Form-item label="商品名称" prop="name">
+          <Input v-model="newContentForm.name" placeholder="请输入内容名称" style="width: 480px"></Input>
+        </Form-item>
+        <Form-item label="所属分类" prop="classifyId">
+          <Cascader :data="classifyDataList" v-model="newContentForm.classifyId" @on-change="changeSelClassify" style="width:30%;" />
+        </Form-item>
+        <Form-item label="牛只所在地" prop="blongArea">
+          <Cascader :data="areadata" change-on-select v-model="newContentForm.blongArea" class="areaClass" :placeholder="pleasett" @on-change="changeSelArea" style="width:30%" :clearable="false"/>
+        </Form-item>
+        <Form-item label="详细地址" prop="addressInfo">
+          <Input v-model="newContentForm.addressInfo" type="textarea" placeholder="请输入链接地址" style="width: 480px"></Input>
+        </Form-item>
+
+        <Divider orientation="left" style="margin-top:2em;"><b>牛只规格</b></Divider>
+        <Form-item label="牛只重量">
+          <Select :key="updateKey" :label-in-value="true" v-model="selectCowWeight" multiple style="width:45%" @on-change="changeSelWeight" placeholder="请选择牛只规格重量(多选)">
+            <Option v-for="item in cowWeightList" :value="item" :key="item" >{{ item }}</Option>
+          </Select>
+          <Button type="success" style="margin-left:3em" @click="confirmCreateVersion">确定生成规格明细</Button>
+        </Form-item>
+        
+        <!-- 表格生成规格 -->
+        <div style="width: 90%; margin:0 auto; margin-bottom:2em;">
+          <div style="font-size:13px; margin:1em 0;"> <Tag color="red">当前规格</Tag></div>
+          <el-table :data="cattleSkuList" >
+            <el-table-column type="index" label="序号" width="65" align="center" />
+            <el-table-column prop="weight" label="规格重量"  width="140" />
+            <el-table-column label="牛只数量" max-width="200">
+              <template #default="scope">
+                <Input v-model="scope.row.count" placeholder="牛只数量" style="width: 90%"></Input>
+              </template>
+            </el-table-column>
+            <el-table-column label="牛只价格" max-width="200">
+              <template #default="scope">
+                <Input v-model="scope.row.price" placeholder="牛只价格" style="width: 90%"></Input>
+              </template>
+            </el-table-column>
+            <el-table-column label="单位" max-width="200">
+              <template #default="scope">
+                <Input v-model="scope.row.unit" placeholder="单位" style="width: 90%"></Input>
+              </template>
+            </el-table-column>
+            <el-table-column label="起售量" max-width="200">
+              <template #default="scope">
+                <Input v-model="scope.row.quantity" placeholder="起售量" style="width: 90%"></Input>
+              </template>
+            </el-table-column>
+            <el-table-column prop="address" label="规格图片" >
+              <template #default="scope">
+                <Upload
+                  ref=""
+                  type="drag"
+                  :action="uploadUrl"
+                  :headers="{'X-AIYANGNIU-SIGNATURE':token}"
+                  :show-upload-list="false"
+                  :on-success="verHandleSuccess"
+                  :before-upload="(e)=>verBeforeUpload(e,scope.$index)"
+                  :format="['jpg','jpeg','png']"
+                  :max-size="2048"
+                  :on-format-error="handleFormatError"
+                  class="upload_version_img">
+                  <div v-if="!scope.row.imgUrl" class="upload_border">
+                    <span>+</span>
+                  </div>
+                  <div v-else style="width:100px;height:100px;"> <img :src="scope.row.imgUrl" style="width:100%; height:100%;" alt=""></div>
+                </Upload>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        
+        <Form-item label="看牛模式" prop="previewType">
+          <Select v-model="newContentForm.previewType" :label-in-value="true" placeholder="请选择看牛模式" style="width:30%">
+            <Option v-for="type in lookCowType" :key="type.typeVal" :value="type.typeVal">{{type.typeLabel}}</Option>
+          </Select>
+        </Form-item>
+
+        <Form-item label="看牛定金" prop="previewMoney">
+          <InputNumber v-model="newContentForm.previewMoney" placeholder="请输入看牛定金"  style="width: 15%" />&nbsp;&nbsp;&nbsp;元 / 头
+        </Form-item>
+
+        <Form-item label="牛只展示图" prop="mainImgData">
+          <div class="demo-upload-list" v-for="item in mainUploadCowImg" :key="item">
+            <template v-if="item.status === 'finished'">
+              <Image :src="item.url" fit="cover" width="100%" height="100%" />
+              <div class="demo-upload-list-cover">
+                <Icon type="ios-eye-outline" @click="handleView(item.url)"></Icon>
+                <Icon type="ios-trash-outline" @click="handleRemove(item)"></Icon>
+              </div>
+            </template>
+            <template v-else>
+              <Progress v-if="item.showProgress" :percent="item.percentage" hide-info></Progress>
+            </template>
+          </div>
+          <Upload
+            ref="mainImgUpload"
+            :headers="{'X-AIYANGNIU-SIGNATURE':token}"
+            :show-upload-list="false"
+            :on-success="handleSuccess"
+            :format="['jpg','jpeg','png']"
+            :on-format-error="handleFormatError"
+            :before-upload="beforeUpload"
+            multiple
+            type="drag"
+            :action="uploadUrl"
+            style="display: inline-block;width:58px;">
+            <div style="width: 58px;height:58px;line-height: 58px;">
+              <Icon type="ios-camera" size="20"></Icon>
+            </div>
+          </Upload>
+          <ImagePreview v-model="preViewImgModel" :preview-list="preViewImg" />
+          <div style="color:#aaa;font-size:12px;">图片格式为 jpg、jpeg、png</div>
+        </Form-item>
+       
+        <Form-item label="牛只详情" prop="content">
+          <BasicEditor ref="editotTool"></BasicEditor>
+        </Form-item>
+      </Form>
+    </div>
+    <div style="text-align:center;margin-bottom:3em;">
+      <Button @click="$router.back()" style="margin-right:3em;">取消返回</Button>
+      <Button type="warning" @click="confirmSubAddNewCow(1)" style="margin-right:3em;">保存在仓库中</Button>
+      <Button type="primary" @click="confirmSubAddNewCow(3)">直接上架牛源</Button>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Message,Modal,Spin } from 'view-ui-plus'
+import { defineComponent, reactive,getCurrentInstance,onMounted,ref} from 'vue'
+import BasicEditor from '@/components/wangEditor/BasicEditor.vue'
+import {compressFileM} from '@/utils/system/uploadCompress.js'
+import areadata from './assets/area.json'
+import api from '@/request/apiConfig' //真实接口配置
+import router from '@/router'
+import {cow} from '@/request/api'
+import { useRouter } from 'vue-router';
+
+export default defineComponent({
+  components:{
+    BasicEditor
+  },
+  setup() {
+    const $router = useRouter();
+    let {proxy} = getCurrentInstance();
+    const token = localStorage.aynUserToken
+    let newContentForm = reactive({
+      classifyId:[] ,blongArea: [], addressInfo:'',previewType:'',previewMoney:null,name:'',mainImgData: [],content:'',classifyOne:'',
+      classifyOneName:'',classifyTwo:'',classifyTwoName:'',classifyThree:'',classifyThreeName:'',province:'',city:'',area:'',picUrl:'',
+    })
+    // 表单验证项
+    let newContentRule = ref({
+      classifyId: [{ required: true, type:'array', message: '请选择所属类别', trigger: 'change' }],
+      blongArea: [{ required: true, type:'array', message: '请选择牛只所在地', trigger: 'change' }],
+      addressInfo:[{ required: true, message: '请填写详细地址', trigger: 'blur' }],
+      previewType:[{ required: true, type:'number', message: '请选择看牛模式', trigger: 'change' }],
+      previewMoney:[
+        { required: true, message: '请输入看牛订金',},
+        { message: '小数保留2位', trigger:'change', pattern:/^(([1-9]\d{0,3})|0)(\.\d{0,2})?$/,}
+      ],
+      name: [{ required: true, message: '请填写内容名称', trigger: 'blur' }],
+      isShow: [{ required: true, message: '请选择展示状态', trigger: 'change' }],
+      mainImgData:[{ required: true, type:'array', message: '请上传牛只主图', trigger: 'change' }],
+    })
+    const uploadUrl = api.apiManager + '/manager/images/upload'
+    let lookCowType = ref([
+      {typeVal:1,typeLabel:'预约看牛:先预约,后到现场看牛'},
+      {typeVal:2,typeLabel:'现场看牛:现场看牛,无需预约'},
+    ])
+    
+
+    // 获取全部牛只分类内容
+    let classifyDataList = ref([])  //分类列表数据
+    async function getAllClassifyData(){
+      await cow.getCattleClassify().then(res=>{
+        if(res.code === 101){
+          let finalData = rinseClassiftData(res.data)
+          console.log('结果数据',finalData);
+          classifyDataList.value = finalData
+        }
+      })
+    }
+
+    // 清洗分类数据
+    function rinseClassiftData(data){
+      data = JSON.parse(JSON.stringify(data).replace(/name/g, 'label'))
+      data = JSON.parse(JSON.stringify(data).replace(/id/g, 'value'))
+      return data
+    }
+    
+    let cowWeightList = ref([])
+    // 获取牛只体重列表
+    function getCowWeightSelList(){
+      cow.getCattleWeightScopes().then(res=>{
+        if(res.code === 101){
+          cowWeightList.value = res.data || []
+        }
+      })
+    }
+
+    // 改变选择的分类
+    function changeSelClassify(val,arr){
+      newContentForm.classifyOne = arr[0]?arr[0].value:''
+      newContentForm.classifyOneName = arr[0]?arr[0].label:''
+      newContentForm.classifyTwo = arr[1]?arr[1].value:''
+      newContentForm.classifyTwoName = arr[1]?arr[1].label:''
+      newContentForm.classifyThree = arr[2]?arr[2].value:''
+      newContentForm.classifyThreeName = arr[2]?arr[2].label:''
+    }
+
+    function changeSelArea(val,arr){
+      newContentForm.province = arr[0]?arr[0].label:''
+      newContentForm.provinceCode = arr[0]?arr[0].value:''
+      newContentForm.city = arr[1]?arr[1].label:''
+      newContentForm.area = arr[2]?arr[2].label:''
+    }
+
+    function beforeUpload(file){
+      // 进行2.5倍系数压缩
+      return (compressFileM(file,2.5))
+    }
+
+    let mainUploadCowImg = ref([])  //主图列表
+    function handleSuccess(res, file, fileList){
+      if(res.code === 101){
+        file.url = res.data
+        newContentForm.mainImgData = mainUploadCowImg.value
+      }
+    }
+
+    let optVersionIndex = ref('')  //记录操作规格下标
+
+    let selectCowWeight = ref([]) //多选牛只数据
+    let cattleSkuList = ref([]) //牛只规格数据
+
+    // 规格单独上传内容
+    function verBeforeUpload(file,ind){
+      console.log('返回内容',file,ind);
+      optVersionIndex = ind
+    }
+    
+    function verHandleSuccess(res, file, fileList){
+      if(res.code == '101'){
+        cattleSkuList.value[optVersionIndex].imgUrl = res.data
+      }
+    }
+
+    function handleFormatError (file) {
+      Message.error({content: '文件格式不正确'})
+    }
+    
+    // 选择牛只规格
+    function changeSelWeight(val){
+      console.log('牛只规格',val);
+      selectCowWeight = val
+    }
+    // 生成规格明细
+    function confirmCreateVersion(){
+      cattleSkuList.value = []
+      selectCowWeight.forEach(item => {
+        let newobj = {}
+        newobj.weight = item.label
+        newobj.selWifghtVal = item.value
+        newobj.count = null
+        newobj.price = null
+        newobj.unit = null
+        newobj.quantity = null
+        newobj.imgUrl = ''
+        cattleSkuList.value.push(newobj)
+      })
+    }
+
+    // 预览上传主图
+    let preViewImg = ref([])
+    let preViewImgModel = ref(false)
+    function handleView (name) {
+      preViewImg.value = [name];
+      preViewImgModel.value = true;
+    }
+
+    function handleRemove (file) {
+      const fileList = proxy.$refs.mainImgUpload.fileList;
+      proxy.$refs.mainImgUpload.fileList.splice(fileList.indexOf(file), 1);
+    }
+
+    const updateKey = ref(0);
+    const pleasett = ref('请选择')
+    // 查询商品详情
+    async function getInitData(){
+      const params ={
+        spuId:$router.currentRoute.value.query.id
+      }
+      await cow.getCattleSpu(params).then(res =>{
+        if (res.code == 101) {
+          Object.assign(newContentForm,res.data);
+          newContentForm.classifyId = [res.data.classifyOne,res.data.classifyTwo,res.data.classifyThree]
+          pleasett.value = res.data.province +' / '+res.data.city+' / '+res.data.area
+          res.data.cattleSkuList.forEach((e,index) =>{   // 规格下拉回显
+            selectCowWeight.value.push(e.weight)
+          })
+          updateKey.value += 1;
+          cattleSkuList.value = res.data.cattleSkuList  // 规格列表回显
+          let imgArr = []    // 预览图回显
+          imgArr = res.data.picUrl.split("#");
+          imgArr.forEach(e=>{
+            mainUploadCowImg.value.push({url:e,status:'finished'})
+          })
+          newContentForm.blongArea = ['北京']
+          newContentForm.mainImgData = mainUploadCowImg.value
+          proxy.$refs.editotTool.valueHtml =res.data.content  // 富文本回显
+        }else{
+          Message.error(res.message)
+        }
+      })
+    }
+
+    // 上架或保存仓库
+    function confirmSubAddNewCow(type){
+      newContentForm.content = proxy.$refs.editotTool.valueHtml  //富文本传值
+      proxy.$refs['newCowForm'].validate(async (valid) => {
+        if (valid) {
+          let sendParams = JSON.parse(JSON.stringify(newContentForm))
+          sendParams.picUrl = proxy.$refs.mainImgUpload.fileList.map(list=>{return list.url}).join('#')
+          sendParams.cattleSkuList = cattleSkuList.value
+          sendParams.skuStatus = type
+          console.log('最终组合的参数',sendParams);
+          let tipFont = ''
+          type == 1? tipFont = '保存至仓库中': tipFont ='直接上架销售'
+          Modal.confirm({
+              title: '温馨提示',
+              content: `<p>您确定将当前牛只信息<b> ${tipFont} </b>?</p>`,
+              onOk: () => {
+                subreq()
+              },
+              onCancel: () => {
+                Message.info('您已取消操作');
+              }
+          });
+          // 提交请求-编辑
+          async function subreq(){
+            Spin.show()
+            await cow.updateCattleSpu(sendParams).then(res=>{
+              Spin.hide()
+              if(res.code == 101){
+                Message.success({content: res.message || '操作成功'})
+                console.log(type,"type")
+                type == 1?router.push('beefInStore'):router.push('beefOnsaleList')
+              }else{
+                Message.error({content: res.message || '操作失败'})
+              }
+            }).catch(e=>{
+              Spin.hide()
+              Message.error({content: e.message || '操作失败'})
+            })
+          }
+          
+        } else {
+          Message.warning({content:'请填写完表单数据'})
+        }
+      })
+    }
+
+    onMounted(async()=>{
+      mainUploadCowImg.value = proxy.$refs.mainImgUpload.fileList
+      getAllClassifyData()  //获取全部分类
+      await getCowWeightSelList() //获取牛只选择体重列表
+      getInitData()
+    })
+
+    return {
+      updateKey,pleasett,uploadUrl,newContentRule,newContentForm,token,classifyDataList,areadata,selectCowWeight,cowWeightList,cattleSkuList,lookCowType,preViewImg,preViewImgModel,mainUploadCowImg,
+      beforeUpload,handleFormatError,handleSuccess,confirmCreateVersion,changeSelWeight,verBeforeUpload,verHandleSuccess,handleView,handleRemove,changeSelClassify,
+      changeSelArea,confirmSubAddNewCow,getInitData
+    }
+  },
+  methods: {
+    
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.editBeef{
+  padding: 1em;
+  .upload_outer_img{
+    max-width: 45%;
+    img{
+      width: 100%; padding: 0;
+    }
+    .upload_border{
+      width: 100%; padding: 3em;
+    }
+  }
+  .upload_version_img{
+    width: 100px; height: 100px;
+    border-radius: 2px; overflow: hidden;
+    .upload_border{
+      line-height: 100px; font-size: 35px; font-weight: bold; color:#ccc;
+      background-color: #f9f9f9;
+    }
+  }
+  .demo-upload-list{
+    display: inline-block;
+    width: 60px;
+    height: 60px;
+    text-align: center;
+    line-height: 60px;
+    border: 1px solid transparent;
+    border-radius: 4px;
+    overflow: hidden;
+    background: #fff;
+    position: relative;
+    box-shadow: 0 1px 1px rgba(0,0,0,.2);
+    margin-right: 4px;
+  }
+  .demo-upload-list img{
+    width: 100%;
+    height: 100%;
+  }
+  .demo-upload-list-cover{
+    display: none;
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    background: rgba(0,0,0,.6);
+  }
+  .demo-upload-list:hover .demo-upload-list-cover{
+    display: block;
+  }
+  .demo-upload-list-cover i{
+    color: #fff;
+    font-size: 20px;
+    cursor: pointer;
+    margin: 0 2px;
+  }
+}
+</style>
+<style lang="scss">
+.editBeef{
+  .ivu-upload-drag{
+    border: 0px;
+  }
+  .areaClass .ivu-input-default::placeholder{
+  color:rgba(0, 0, 0, 0.685) !important;
+}
+}
+</style>

+ 304 - 0
src/views/cattleTrdeValue/index.vue

@@ -0,0 +1,304 @@
+<template>
+  <div class="cattleTrdeValue">
+    <Row :gutter="8">
+      <Col span="4">
+        <Input v-model="typesearchFilter.cattleBreed" placeholder="请输入牛只品种" clearable/>
+      </Col>
+      <Col span="4">
+        <Button type="primary" @click="getData">查询信息</Button>
+      </Col>
+      <Col span="16" style="text-align: right;">
+        <el-button type="primary" @click="addContent">新增牛只品种</el-button>
+      </Col>
+    </Row>
+
+    <el-table :data="tabData.data" style="width: 100%; margin-top:3.5em;" border>
+      <!-- <el-table-column label="序号" type="index" width="70" align="center"></el-table-column> -->
+      <el-table-column prop="cattleBreed" label="牛只品种" align="center"> </el-table-column>
+      <el-table-column label="牛只类型" align="center">
+        <template #default="scope">
+          {{scope.row.cattleType||'无'}}
+        </template>
+      </el-table-column>
+      <el-table-column label="日增重(公斤)" align="center">
+        <template #default="scope"> 
+         {{scope.row.dailyGain}}
+        </template>
+      </el-table-column>
+      <el-table-column label="平均日增重(公斤)" align="center">
+        <template #default="scope"> 
+         {{scope.row.dailyGainAvg}}
+        </template>
+      </el-table-column>
+      <el-table-column label="胴体率(%)" align="center">
+        <template #default="scope"> 
+         {{scope.row.carcassPercent || '0'}}
+        </template>
+      </el-table-column>
+      <el-table-column label="平均胴体率(%)" align="center">
+        <template #default="scope"> 
+         {{scope.row.carcassPercentAvg || '0'}}
+        </template>
+      </el-table-column>
+      <el-table-column label="净肉率(%)" align="center">
+        <template #default="scope"> 
+         {{scope.row.meatPercent || '0'}}
+        </template>
+      </el-table-column>
+       <el-table-column label="平均净肉率(%)" align="center">
+        <template #default="scope"> 
+         {{scope.row.meatPercentAvg || '0'}}
+        </template>
+      </el-table-column>
+      <el-table-column label="排序" prop="sort" width="70" align="center"></el-table-column>
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <!--按钮-->
+          <el-button type="primary" size="small" @click="updateContent(scope.row)">编辑</el-button>&nbsp;
+          <el-button type="danger" size="small" @click="delContent(scope.row.id)">删除</el-button>&nbsp;
+        </template>
+      </el-table-column>
+    </el-table>
+    
+    <div class="page_style">
+      <Page :total="tabData.total" :model-value="currentPage" show-elevator show-total @on-change="changePage" />
+    </div>
+    <!-- 新增分类信息 -->
+    <Modal v-model="optClassifyModal" :title="optClassifyModalTitle" width="35%">
+      <Form ref="optClassifyFormRef" :model="optClassifyForm" :label-width="140">
+        <FormItem prop="cattleBreed" label="牛只品种:">
+          <Input v-model="optClassifyForm.cattleBreed" placeholder="请输入牛只品种" style="width:100%;" clearable></Input>
+        </FormItem>
+        <FormItem prop="cattleType" label="牛只类型:">
+          <Select :label-in-value="true" v-model="optClassifyForm.cattleType" style="width:100%" placeholder="请选择" clearable>
+            <Option value="公牛">公牛</Option>
+            <Option value="母牛">母牛</Option>
+          </Select>
+        </FormItem>
+        <FormItem prop="cattleBreed" label="排序:">
+          <Input type='number' v-model="optClassifyForm.sort" placeholder="请输入序号" style="width:100%;"></Input>
+        </FormItem>
+        <FormItem prop="dailyGain" label="日增重(公斤):">
+          <Input v-model="optClassifyForm.dailyGain" placeholder="请输入日增重" style="width:100%;" clearable></Input>
+        </FormItem>
+        <FormItem prop="dailyGainAvg" label="平均日增重(公斤):">
+          <InputNumber :min="0" v-model="optClassifyForm.dailyGainAvg" placeholder="请输入日增重" style="width:100%;" />
+        </FormItem>
+        <FormItem prop="carcassPercent" label="胴体率(%):">
+          <Input v-model="optClassifyForm.carcassPercent" placeholder="请输入胴体率" style="width:100%;" clearable></Input>
+        </FormItem>
+        <FormItem prop="carcassPercentAvg" label="平均胴体率(%):">
+          <InputNumber :min="0" v-model="optClassifyForm.carcassPercentAvg" placeholder="请输入胴体率" style="width:100%;" />
+        </FormItem>
+        <FormItem prop="meatPercent" label="净肉率(%):">
+          <Input v-model="optClassifyForm.meatPercent" placeholder="请输入净肉率" style="width:100%;" clearable></Input>
+        </FormItem>
+        <FormItem prop="meatPercentAvg" label="平均净肉率(%):">
+          <InputNumber :min="0" v-model="optClassifyForm.meatPercentAvg" placeholder="请输入净肉率" style="width:100%;" />
+        </FormItem>
+        <Row>
+          <Col :span="12">
+            <FormItem prop="viceCowhide" label="牛皮占比(%):">
+              <InputNumber :min="0" v-model="optClassifyForm.viceCowhide" placeholder="请输入牛皮占比" style="width:100%;" />
+            </FormItem>
+          </Col>
+          <Col :span="12">
+            <FormItem prop="viceOil" label="杂油占比(%):">
+              <InputNumber :min="0" v-model="optClassifyForm.viceOil" placeholder="请输入杂油占比" style="width:100%;" />
+            </FormItem>
+          </Col>
+        </Row>
+        <Row>
+          <Col :span="12">
+            <FormItem prop="viceHead" label="牛头占比(%):">
+              <InputNumber :min="0" v-model="optClassifyForm.viceHead" placeholder="请输入牛头占比" style="width:100%;" />
+            </FormItem>
+          </Col>
+          <Col :span="12">
+            <FormItem prop="viceHoof" label="牛蹄占比(%):">
+              <InputNumber :min="0" v-model="optClassifyForm.viceHoof" placeholder="请输入牛蹄占比" style="width:100%;" />
+            </FormItem>
+          </Col>
+        </Row>
+        <Row>
+          <Col :span="12">
+            <FormItem prop="viceOmasum" label="毛肚占比(%):">
+              <InputNumber :min="0" v-model="optClassifyForm.viceOmasum" placeholder="请输入毛肚占比" style="width:100%;" />
+            </FormItem>
+          </Col>
+          <Col :span="12">
+            <FormItem prop="viceLiver" label="牛肝占比(%):">
+              <InputNumber :min="0" v-model="optClassifyForm.viceLiver" placeholder="请输入牛肝占比" style="width:100%;" />
+            </FormItem>
+          </Col>
+        </Row>
+        <Row>
+          <Col :span="12">
+            <FormItem prop="viceIntestine" label="牛肠占比(%):">
+              <InputNumber :min="0" v-model="optClassifyForm.viceIntestine" placeholder="请输入牛肠占比" style="width:100%;" />
+            </FormItem>
+          </Col>
+          <Col :span="12">
+            <FormItem prop="viceLung" label="牛肺占比(%):">
+              <InputNumber :min="0" v-model="optClassifyForm.viceLung" placeholder="请输入牛肺占比" style="width:100%;" />
+            </FormItem>
+          </Col>
+        </Row>
+        <Row>
+          <Col :span="12">
+            <FormItem prop="viceTripe" label="牛百叶占比(%):">
+              <InputNumber :min="0" v-model="optClassifyForm.viceTripe" placeholder="请输入牛百叶占比" style="width:100%;" />
+            </FormItem>
+          </Col>
+          <Col :span="12">
+            <FormItem prop="viceHeart" label="牛心占比(%):">
+              <InputNumber :min="0" v-model="optClassifyForm.viceHeart" placeholder="请输入牛心占比" style="width:100%;" />
+            </FormItem>
+          </Col>
+        </Row>
+      </Form>
+      <template #footer>
+        <div style="text-align:center;">
+          <Button style="margin-right:5em;" @click="optClassifyModal = false">取消操作</Button>
+          <Button type="primary" @click="submitOptClassify">确定提交</Button>
+        </div>
+      </template>
+    </Modal>
+  </div>
+</template>
+
+<script>
+import { defineComponent,ref,reactive,onMounted } from 'vue'
+import {item} from '@/request/api'
+import { ElMessage } from 'element-plus'
+import { tr } from 'element-plus/es/locale'
+export default defineComponent({
+  components:{
+    
+  },
+  setup() {
+    let optClassifyModal = ref(false)
+    let optClassifyModalTitle = ref('新增')
+    let optClassifyForm = reactive({carcassPercent:'',carcassPercentAvg:'',cattleBreed :'',cattleType :'',dailyGain :'',dailyGainAvg :'',meatPercent :'',
+    meatPercentAvg:'',viceCowhide :'',viceHead :'',viceHeart :'',viceHoof :'',viceIntestine :'',viceLiver:'',viceLung :'',viceOil :'',viceOmasum :'',viceTripe :'',sort:0})
+
+    let tabData = ref({})
+    let typesearchFilter = reactive({limit:10,offset:0,cattleBreed:''})
+    //获取列表内容
+    async function getData(){
+      await item.cattleDeelList(typesearchFilter).then(res =>{
+        if (res.code == 101) {
+          tabData.value = res || {}
+        }
+      })
+    }
+
+    let currentPage = ref(1)
+    //更改页码
+    function changePage (page) {
+      if(tabData.value.limit){
+        typesearchFilter.offset = (page -1) *  tabData.value.limit  //更新偏移量
+        currentPage = page  //切换当前页码
+        getData()//获取广告数据
+      }
+    }
+
+    // 删除内容
+    function delContent(id){
+      this.$Modal.confirm({
+        title: '温馨提示',
+        content: '您确定删除当前内容吗?',
+        onOk:async () => {
+          await item.deleteCattleDeel({id:id}).then(res=>{
+            if(res.code == 101){
+              ElMessage({
+                message: res.message || '操作成功!',
+                type: 'success',
+              })
+              this.changePage(1)  //重新获取列表
+            }else{
+              ElMessage({
+                message: res.message || '操作失败!',
+                type: 'warning',
+              })
+            }
+          }).catch(e=>{
+            ElMessage({
+              message: e.message || '添加失败!',
+              type: 'error',
+            })
+          })
+        },
+        onCancel: () => {
+          this.$Message.info('您已取消操作!');
+        }
+      });
+    }
+    // 提交操作分类
+    function submitOptClassify(){
+      let sendParams = JSON.parse(JSON.stringify(optClassifyForm))
+      let reqFn = null
+      if(optClassifyForm.id && optClassifyForm.id != null){
+        reqFn = item.updateCattleDeel
+      }else{
+        reqFn = item.createCattleDeel
+      }
+      reqFn(sendParams).then(res=>{
+        if(res.code === 101){
+          optClassifyModal.value = false
+          ElMessage({
+            message: res.message || '操作成功!',
+            type: 'success',
+          })
+          getData()
+        }else{
+          ElMessage({
+            message: res.message || '操作失败!',
+            type: 'warning',
+          })
+        }
+      }).catch(e=>{
+        ElMessage({
+          message: res.message || '操作失败!',
+          type: 'warning',
+        })
+      })
+    }
+    //编辑
+    function updateContent(data){
+      optClassifyModal.value = true
+      Object.assign(optClassifyForm,data)
+      optClassifyModalTitle.value = '编辑'
+    }
+    //新增
+    function addContent(){
+      Object.assign(optClassifyForm,{id:null,carcassPercent:'',carcassPercentAvg:'',cattleBreed :'',cattleType :'',dailyGain :'',dailyGainAvg :'',meatPercent :'',
+      meatPercentAvg:'',viceCowhide :'',viceHead :'',viceHeart :'',viceHoof :'',viceIntestine :'',viceLiver:'',viceLung :'',viceOil :'',viceOmasum :'',viceTripe :'',sort:0})
+      console.log(optClassifyForm,'optClassifyForm')
+      optClassifyModal.value = true
+      optClassifyModalTitle.value = '新增'
+    }
+    onMounted(()=>{
+      getData() 
+    })
+
+    return {
+      typesearchFilter,optClassifyModal,optClassifyModalTitle,optClassifyForm,
+      getData,tabData,changePage,currentPage,updateContent,delContent,submitOptClassify,addContent
+    }
+  },
+  methods:{
+    
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.cattleTrdeValue{
+  padding: 1em;
+  .page_style{
+    text-align: right; margin-top: 1em;
+    background-color: var(--system-container-background);
+  }
+}
+</style>

+ 887 - 0
src/views/cattleTrdeValue/recondsInfo.vue

@@ -0,0 +1,887 @@
+<template>
+  <div class="recondsInfo">
+    <!-- <Row :gutter="8">
+      <Col span="4">
+        <Input v-model="typesearchFilter.cattleBreed" placeholder="请输入牛只品种" clearable/>
+      </Col>
+      <Col span="4">
+        <Button type="primary" @click="getData">查询信息</Button>
+      </Col>
+    </Row> -->
+
+    <el-table :data="tabData.data" style="width: 100%;" border>
+      <el-table-column label="序号" type="index" width="70" align="center"></el-table-column>
+      <el-table-column prop="remarks" label="记录名称" align="center"> </el-table-column>
+      <el-table-column prop="cattleBreed" label="牛只品种" align="center"> </el-table-column>
+      <el-table-column label="牛只类型" align="center">
+        <template #default="scope">
+          {{scope.row.cattleType||'无'}}
+        </template>
+      </el-table-column>
+      <el-table-column prop="cattleNum" label="牛只数量" align="center"> </el-table-column>
+      <el-table-column label="测算时间" align="center">
+        <template #default="scope">
+          {{ moment(scope.row.addTime).format('yyyy-MM-DD hh:mm:ss')}}
+        </template>
+      </el-table-column>
+      <el-table-column prop="userName" label="用户名" align="center"> </el-table-column>
+      <el-table-column label="操作" width="200"  fixed="right">
+        <template  #default="scope">
+          <el-button size="small" type="primary" @click="lookDetail(scope.row)">查看详情</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="page_style">
+      <Page :total="tabData.total" :model-value="currentPage" show-elevator show-total @on-change="changePage" />
+    </div>
+    <!-- 查看详情 -->
+    <Modal v-model="contentTypeModel" title="查看详情" ok-text="确定" width="50%">
+      <el-divider content-position="center">牛只采购</el-divider>
+      <Row>
+        <Col span="5">
+          <span>牛只品种:{{info.cattleBreed}}</span>
+        </Col>
+        <Col span="5">
+          <span>牛只类型:{{info.cattleType}}</span>
+        </Col>
+        <Col span="5">
+          <span>牛只数量:{{info.cattleNum|| 0}}头</span>
+        </Col>
+        <Col span="5">
+          <span>采购重量:{{info.purchaseWeight|| 0}}公斤/头</span>
+        </Col>
+        <Col span="4">
+          <span>采购单价:{{info.purchasePrice|| 0}}元/公斤</span>
+        </Col>
+      </Row>
+      <el-table :data="arrOne" style="width: 100%;margin:10px 0" border stripe>
+        <el-table-column prop="cattleBreed" label="牛只品种" align="center"> </el-table-column>
+        <el-table-column prop="cattleType" label="牛只类型" align="center"> </el-table-column>
+        <el-table-column prop="weight" label="活牛重(公斤)" align="center"> </el-table-column>
+        <el-table-column prop="purchasePrice" label="单价(元/公斤)" align="center"> </el-table-column>
+        <el-table-column prop="totalMoney" label="采购投资(万元)" align="center"> </el-table-column>
+      </el-table>
+      <el-divider content-position="center">牛只育肥</el-divider>
+      <Row>
+        <Col span="5">
+          <span>是否育肥:{{info.isYf == 1?'是':'否'}}</span>
+        </Col>
+        <Col span="5" v-if="info.isYf == 1">
+          <span>育肥周期:{{info.growCycle|| 0}}天</span>
+        </Col>
+        <Col span="5" v-if="info.isYf == 1">
+          <span>出栏重量:{{info.targetWeight|| 0}}公斤/头</span>
+        </Col>
+        <Col span="5" v-if="info.isYf == 1">
+          <span>育肥运输:{{info.growTransport|| 0}}元/头</span>
+        </Col>
+        <Col span="4" v-if="info.isYf == 1">
+          <span>饲草费用:{{info.grassFee|| 0}}元/头/天</span>
+        </Col>
+        <Col span="5" v-if="info.isYf == 1" style="margin-top:10px;">
+          <span>人工费用:{{info.laborFee|| 0}}元/头/天</span>
+        </Col>
+        <Col span="5" v-if="info.isYf == 1" style="margin-top:10px;">
+          <span>场地费用:{{info.spaceFee|| 0}}元/头/天</span>
+        </Col>
+      </Row>
+      <el-table :data="arrOne" style="width: 100%;margin:10px 0" border stripe v-if="info.isYf == 1">
+        <el-table-column prop="transporFee" label="运输费用(万元)" align="center"> 
+          <template #default="scope">
+            <span>{{scope.row.transporFee.toFixed(2)}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="dailyGain" label="日增重(公斤/天)" align="center">
+          <template #default="scope">
+            <span>{{scope.row.dailyGain.toFixed(2)}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="cost" label="育肥成本(元/公斤)" align="center">
+          <template #default="scope">
+            <span>{{scope.row.cost.toFixed(2)}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="invest" label="育肥投资(万元)" align="center">
+          <template #default="scope">
+            <span>{{scope.row.invest.toFixed(2)}}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-divider content-position="center">牛只销售</el-divider>
+      <el-tabs type="border-card">
+        <el-tab-pane label="活牛销售">
+          <Row>
+            <Col span="5">
+              <span>销售运输:{{info.saleCattleTransport || 0}}元/头</span>
+            </Col>
+            <Col span="5">
+              <span>销售重量:{{info.saleCattleWeight|| 0}}公斤/头</span>
+            </Col>
+            <Col span="5">
+              <span>销售单价:{{info.saleCattlePrice|| 0}}元/公斤</span>
+            </Col>
+            <Col span="5">
+              <span>采销差价:{{info.priceDifference|| 0}}元/公斤</span>
+            </Col>
+          </Row>
+          <el-table :data="arrTwo" style="width: 100%;margin:10px 0" border stripe>
+            <el-table-column prop="transporFee" label="运输费用(万元)" align="center"> 
+              <template #default="scope">
+                <span>{{scope.row.transporFee.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cowWeight" label="活牛重(公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.cowWeight.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cost" label="单价(元/公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.price.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="收入(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.income.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="销售毛利(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.salesProfit.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="毛利率(%)" align="center">
+              <template #default="scope">
+                <span :style="scope.row.rate<0?'color:red':''">{{scope.row.rate.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div>胴体销售预估数据:</div>
+          <el-table :data="arrTwo" style="width: 100%;margin:10px 0" border stripe>
+            <el-table-column prop="transporFee" label="运输费用(万元)" align="center"> 
+              <template #default="scope">
+                <span>{{scope.row.transporFee.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cowWeight" label="胴体重(公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.dtWeight.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cost" label="单价(元/公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.dtprice.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="收入(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.dtincome.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="销售毛利(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.dtsalesProfit.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="毛利率(%)" align="center">
+              <template #default="scope">
+                <span :style="scope.row.dtRate<0?'color:red':''">{{scope.row.dtRate.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div>牛肉销售预估数据:</div>
+          <el-table :data="arrTwo" style="width: 100%;margin:10px 0" border stripe>
+            <el-table-column prop="transporFee" label="运输费用(万元)" align="center"> 
+              <template #default="scope">
+                <span>{{scope.row.transporFee.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cowWeight" label="净肉重(公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefWeight.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cost" label="单价(元/公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefprice.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="收入(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefincome.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="销售毛利(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefsalesProfit.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="毛利率(%)" align="center">
+              <template #default="scope">
+                <span :style="scope.row.beefRate<0?'color:red':''">{{scope.row.beefRate.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-tab-pane>
+        <el-tab-pane label="胴体销售">
+          <Row>
+            <Col span="5">
+              <span>销售重量:{{info.saleCattleWeight|| 0}}公斤/头</span>
+            </Col>
+            <Col span="5">
+              <span>平均胴体率:{{info.carcassPercentAvg|| 0}}%</span>
+            </Col>
+            <Col span="5">
+              <span>胴体单价:{{info.saleCarcassPrice|| 0}}元/公斤</span>
+            </Col>
+            <Col span="5">
+              <span>加工费:{{info.saleCarcassCutPrice|| 0}}元/头</span>
+            </Col>
+          </Row>
+          <el-table :data="arrThree" style="width: 100%;margin:10px 0" border stripe>
+            <el-table-column prop="transporFee" label="屠宰费用(万元)" align="center"> 
+              <template #default="scope">
+                <span>{{scope.row.butcherFee.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="transporFee" label="运输费用(万元)" align="center"> 
+              <template #default="scope">
+                <span>{{scope.row.transporFee.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cowWeight" label="胴体重(公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.dtWeight.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cost" label="单价(元/公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.price.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="收入(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.income.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="销售毛利(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.salesProfit.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="毛利率(%)" align="center">
+              <template #default="scope">
+                <span :style="scope.row.rate<0?'color:red':''">{{scope.row.rate.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div>活牛销售预估数据:</div>
+          <el-table :data="arrThree" style="width: 100%;margin:10px 0" border stripe>
+            <el-table-column prop="transporFee" label="运输费用(万元)" align="center"> 
+              <template #default="scope">
+                <span>{{scope.row.transporFee.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cowWeight" label="活牛重(公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.cowWeight.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cost" label="单价(元/公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.cowprice.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="收入(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.cowincome.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="销售毛利(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.cowsalesProfit.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="毛利率(%)" align="center">
+              <template #default="scope">
+                <span :style="scope.row.cowrate<0?'color:red':''">{{scope.row.cowrate.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div>牛肉销售预估数据:</div>
+          <el-table :data="arrThree" style="width: 100%;margin:10px 0" border stripe>
+            <el-table-column prop="transporFee" label="运输费用(万元)" align="center"> 
+              <template #default="scope">
+                <span>{{scope.row.transporFee.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cowWeight" label="净肉重(公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefWeight.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cost" label="单价(元/公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefprice.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="收入(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefincome.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="销售毛利(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefsalesProfit.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="毛利率(%)" align="center">
+              <template #default="scope">
+                <span :style="scope.row.beefRate<0?'color:red':''">{{scope.row.beefRate.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-tab-pane>
+        <el-tab-pane label="牛肉销售">
+           <Row>
+            <Col span="5">
+              <span>销售重量:{{info.saleCattleWeight|| 0}}公斤/头</span>
+            </Col>
+            <Col span="5">
+              <span>平均净肉率:{{info.meatPercentAvg || 0}}%</span>
+            </Col>
+            <Col span="5">
+              <span>牛肉单价:{{info.saleBeefPrice|| 0}}元/公斤</span>
+            </Col>
+            <Col span="5">
+              <span>加工费:{{info.saleBeefCutPrice|| 0}}元/头</span>
+            </Col>
+          </Row>
+          <el-table :data="arrFour" style="width: 100%;margin:10px 0" border stripe>
+            <el-table-column prop="transporFee" label="屠宰费用(万元)" align="center"> 
+              <template #default="scope">
+                <span>{{scope.row.butcherFee.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="transporFee" label="运输费用(万元)" align="center"> 
+              <template #default="scope">
+                <span>{{scope.row.transporFee.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cowWeight" label="净肉重(公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefWeight.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cost" label="单价(元/公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefprice.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="收入(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefincome.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="销售毛利(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.beefsalesProfit.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="毛利率(%)" align="center">
+              <template #default="scope">
+                <span :style="scope.row.beefRate<0?'color:red':''">{{scope.row.beefRate.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div>活牛销售预估数据:</div>
+          <el-table :data="arrFour" style="width: 100%;margin:10px 0" border stripe>
+            <el-table-column prop="transporFee" label="运输费用(万元)" align="center"> 
+              <template #default="scope">
+                <span>{{scope.row.transporFee.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cowWeight" label="活牛重(公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.cowWeight.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cost" label="单价(元/公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.cowprice.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="收入(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.cowincome.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="销售毛利(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.cowsalesProfit.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="毛利率(%)" align="center">
+              <template #default="scope">
+                <span :style="scope.row.cowrate<0?'color:red':''">{{scope.row.cowrate.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div>胴体销售预估数据:</div>
+          <el-table :data="arrFour" style="width: 100%;margin:10px 0" border stripe>
+            <el-table-column prop="transporFee" label="运输费用(万元)" align="center"> 
+              <template #default="scope">
+                <span>{{scope.row.transporFee.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cowWeight" label="胴体重(公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.dtWeight.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="cost" label="单价(元/公斤)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.dtprice.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="收入(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.dtincome.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="销售毛利(万元)" align="center">
+              <template #default="scope">
+                <span>{{scope.row.dtsalesProfit.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="invest" label="毛利率(%)" align="center">
+              <template #default="scope">
+                <span :style="scope.row.dtRate<0?'color:red':''">{{scope.row.dtRate.toFixed(2)}}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-tab-pane>
+        <el-tab-pane label="牛副">
+          <Row style="font-weight: 600;" class="rowStyle">
+            <Col span="4">序号</Col>
+            <Col span="5">部位</Col>
+            <Col span="5">牛副占比</Col>
+            <Col span="5">价格(元/公斤)</Col>
+            <Col span="5">价值(元)</Col>
+          </Row>
+          <Row class="rowStyle">
+            <Col span="4">1</Col>
+            <Col span="5">牛皮</Col>
+            <Col span="5">{{infoOne.viceCowhide}}</Col>
+            <Col span="5">{{info.viceCowhidePrice}}</Col>
+            <Col span="5">{{infoVice.viceCowhideMoney?infoVice.viceCowhideMoney.toFixed(2):0}}</Col>
+          </Row>
+          <Row class="rowStyle">
+            <Col span="4">2</Col>
+            <Col span="5">杂油</Col>
+            <Col span="5">{{infoOne.viceOil}}</Col>
+            <Col span="5">{{info.viceOilPrice}}</Col>
+            <Col span="5">{{infoVice.viceOilMoney?infoVice.viceOilMoney.toFixed(2):0}}</Col>
+          </Row>
+          <Row class="rowStyle">
+            <Col span="4">3</Col>
+            <Col span="5">牛头</Col>
+            <Col span="5">{{infoOne.viceHead}}</Col>
+            <Col span="5">{{info.viceHeadPrice}}</Col>
+            <Col span="5">{{infoVice.viceHeadMoney?infoVice.viceHeadMoney.toFixed(2):0}}</Col>
+          </Row>
+          <Row class="rowStyle">
+            <Col span="4">4</Col>
+            <Col span="5">牛蹄</Col>
+            <Col span="5">{{infoOne.viceHoof}}</Col>
+            <Col span="5">{{info.viceHoofPrice}}</Col>
+            <Col span="5">{{infoVice.viceOilMoney?infoVice.viceOilMoney.toFixed(2):0}}</Col>
+          </Row>
+          <Row class="rowStyle">
+            <Col span="4">5</Col>
+            <Col span="5">毛肚</Col>
+            <Col span="5">{{infoOne.viceOmasum}}</Col>
+            <Col span="5">{{info.viceOmasumPrice}}</Col>
+            <Col span="5">{{infoVice.viceOmasumMoney?infoVice.viceOmasumMoney.toFixed(2):0}}</Col>
+          </Row>
+          <Row class="rowStyle">
+            <Col span="4">6</Col>
+            <Col span="5">牛肝</Col>
+            <Col span="5">{{infoOne.viceLiver}}</Col>
+            <Col span="5">{{info.viceLiverPrice}}</Col>
+            <Col span="5">{{infoVice.viceLiverMoney?infoVice.viceLiverMoney.toFixed(2):0}}</Col>
+          </Row>
+          <Row class="rowStyle">
+            <Col span="4">7</Col>
+            <Col span="5">牛肠</Col>
+            <Col span="5">{{infoOne.viceIntestine}}</Col>
+            <Col span="5">{{info.viceIntestinePrice}}</Col>
+            <Col span="5">{{infoVice.viceIntestineMoney?infoVice.viceIntestineMoney.toFixed(2):0}}</Col>
+          </Row>
+          <Row class="rowStyle">
+            <Col span="4">8</Col>
+            <Col span="5">牛肺</Col>
+            <Col span="5">{{infoOne.viceLung}}</Col>
+            <Col span="5">{{info.viceLungPrice}}</Col>
+            <Col span="5">{{infoVice.viceLungMoney?infoVice.viceLungMoney.toFixed(2):0}}</Col>
+          </Row>
+          <Row class="rowStyle">
+            <Col span="4">9</Col>
+            <Col span="5">牛百叶</Col>
+            <Col span="5">{{infoOne.viceTripe}}</Col>
+            <Col span="5">{{info.viceTripePrice}}</Col>
+            <Col span="5">{{infoVice.viceTripeMoney?infoVice.viceTripeMoney.toFixed(2):0}}</Col>
+          </Row>
+          <Row class="rowStyle">
+            <Col span="4">10</Col>
+            <Col span="5">牛心</Col>
+            <Col span="5">{{infoOne.viceHeart}}</Col>
+            <Col span="5">{{info.viceHeratPrice}}</Col>
+            <Col span="5">{{infoVice.viceHeratMoney?infoVice.viceHeratMoney.toFixed(2):0}}</Col>
+          </Row>
+          <Row style="font-weight: 600;" class="rowStyle">
+            <Col span="4">合计</Col>
+            <Col span="5"></Col>
+            <Col span="5">{{infoVice.totalWeight?infoVice.totalWeight.toFixed(2):0}}</Col>
+            <Col span="5"></Col>
+            <Col span="5">{{infoVice.totalMoney?infoVice.totalMoney.toFixed(2):0}}</Col>
+          </Row>
+        </el-tab-pane>
+      </el-tabs>
+      <template #footer>
+        <div style="text-align:center;">
+          <el-button @click="contentTypeModel = false">关闭</el-button>
+        </div>
+      </template>
+    </Modal> 
+  </div>
+</template>
+
+<script>
+import { defineComponent,ref,reactive,onMounted } from 'vue'
+import {item} from '@/request/api'
+import { ElMessage } from 'element-plus'
+import moment from 'moment'
+export default defineComponent({
+  components:{
+    
+  },
+  setup() {
+    let tabData = ref({})
+    let typesearchFilter = reactive({limit:10,offset:0,cattleBreed:''})
+    //获取列表内容
+    async function getData(){
+      await item.getCattleDealValuePage(typesearchFilter).then(res =>{
+        if (res.code == 101) {
+          tabData.value = res || {}
+        }
+      })
+    }
+
+    let currentPage = ref(1)
+    //更改页码
+    function changePage (page) {
+      if(tabData.value.limit){
+        typesearchFilter.offset = (page -1) *  tabData.value.limit  //更新偏移量
+        currentPage = page  //切换当前页码
+        getData()//获取广告数据
+      }
+    }
+    let contentTypeModel = ref(false)
+    let info = ref({})
+    let arrOne = ref([])
+    let arrTwo = ref([])
+    let arrThree = ref([])
+    let arrFour = ref([])
+    let infoVice = ref({})
+    //查看详情
+    async function lookDetail(data){
+      info.value = {}
+      info.value = data
+      if(data.targetWeight){
+        info.value.isYf = 1
+      }else{
+        info.value.isYf = 0
+      }
+      info.value.priceDifference = (Number(data.saleCattlePrice)-Number(data.purchasePrice)).toFixed(2)
+      await getcattleData(data.cattleBreed,data.cattleType)
+      await calculateAll(info.value)
+      contentTypeModel.value = true
+    }
+    //计算育肥,销售,牛副信息
+    function calculateAll(data){
+      arrOne.value = []
+      arrTwo.value = []
+      arrThree.value = []
+      arrFour.value = []
+      infoVice.value = {}
+      // 计算牛只采购,牛只育肥
+      let newobj = {}
+      newobj.cattleBreed = data.cattleBreed
+      newobj.cattleType = data.cattleType
+      newobj.purchasePrice = data.purchasePrice
+      if(data.cattleNum && data.cattleNum!= '' && data.purchaseWeight && data.purchaseWeight !=''){
+        newobj.weight = Number(data.cattleNum)*Number(data.purchaseWeight)
+      }else{
+        newobj.weight = 0
+      }
+      if(data.cattleNum && data.cattleNum!= '' && data.purchaseWeight && data.purchaseWeight !='' && data.purchasePrice && data.purchasePrice !=''){
+        newobj.totalMoney = (Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000
+      }else{
+        newobj.totalMoney = 0
+      }
+      if(data.cattleNum && data.cattleNum != '' && data.growTransport && data.growTransport !=''){
+        newobj.transporFee = (Number(data.cattleNum)*Number(data.growTransport))/10000  //运输费用
+      }else{
+        newobj.transporFee = 0
+      }
+      if(data.targetWeight && data.targetWeight != '' && data.purchaseWeight && data.purchaseWeight!= '' &&data.growCycle && data.growCycle!=''){
+        newobj.dailyGain = (Number(data.targetWeight)-Number(data.purchaseWeight))/Number(data.growCycle) //日增重
+      }else{
+        newobj.dailyGain = 0
+      }
+      if(data.grassFee && data.grassFee !='' && data.laborFee && data.laborFee!='' && data.spaceFee && data.spaceFee!='' && data.growCycle && data.growCycle!=''){
+        newobj.invest = (((Number(data.grassFee)+Number(data.laborFee)+Number(data.spaceFee))*Number(data.growCycle)*Number(data.cattleNum))/10000)+newobj.transporFee  //育肥投资
+      }else{
+        newobj.invest = 0
+      }
+      if(newobj.invest != ''){
+        newobj.cost = (newobj.invest * 10000)/((Number(data.targetWeight)-Number(data.purchaseWeight))*Number(data.cattleNum))
+      }else{
+        newobj.cost = 0
+      }
+      arrOne.value.push(newobj)
+      // 计算牛只销售--活牛销售
+      let infoXs = {dtRate:0,beefRate:0,cowWeight:0,price:0,income:0,rate:0,priceDifference:0}
+      if(data.saleCattleTransport && data.saleCattleTransport != ''){
+        infoXs.transporFee = (Number(data.cattleNum)*Number(data.saleCattleTransport))/10000 //运输费用
+      }else{
+        infoXs.transporFee = 0
+      }
+      if(data.saleCattlePrice != '' && data.saleCattleWeight !=''){
+        infoXs.cowWeight = Number(data.cattleNum)*Number(data.saleCattleWeight)
+        infoXs.price = Number(data.saleCattlePrice)  //销售单价
+        infoXs.income = (infoXs.cowWeight*Number(data.saleCattlePrice))/10000   //收入万元
+        infoXs.salesProfit = infoXs.income-((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)-newobj.invest-infoXs.transporFee//销售毛利(万元)
+        infoXs.rate = (infoXs.salesProfit/infoXs.income)*100  //毛利率
+        infoXs.priceDifference = (Number(data.saleCattlePrice)-Number(data.purchasePrice)).toFixed(2)
+      }
+      // 实际平均胴体率,实际平均净肉率
+      if(data.saleCattleWeight && data.saleCattleWeight != ''){
+        if(data.saleCattleWeight >= 800){
+          data.carcassPercentAvg  = infoOne.value.carcassPercentAvgOne + 2
+          data.meatPercentAvg  = infoOne.value.meatPercentAvgOne + 2
+        }else if(data.saleCattleWeight >= 750 && data.saleCattleWeight < 800){
+          data.carcassPercentAvg  = infoOne.value.carcassPercentAvgOne + 1.5
+          data.meatPercentAvg  = infoOne.value.meatPercentAvgOne + 1.5
+        }else if(data.saleCattleWeight >= 700 && data.saleCattleWeight < 750){
+          data.carcassPercentAvg  = infoOne.value.carcassPercentAvgOne + 1
+          data.meatPercentAvg  = infoOne.value.meatPercentAvgOne + 1
+        }else if(data.saleCattleWeight >= 650 && data.saleCattleWeight < 700){
+          data.carcassPercentAvg  = infoOne.value.carcassPercentAvgOne + 0.5
+          data.meatPercentAvg  = infoOne.value.meatPercentAvgOne + 0.5
+        }else if(data.saleCattleWeight >= 600 && data.saleCattleWeight < 650){
+          data.carcassPercentAvg  = infoOne.value.carcassPercentAvgOne 
+          data.meatPercentAvg  = infoOne.value.meatPercentAvgOne
+        }else if(data.saleCattleWeight >= 550 && data.saleCattleWeight < 600){
+          data.carcassPercentAvg  = infoOne.value.carcassPercentAvgOne -1
+          data.meatPercentAvg  = infoOne.value.meatPercentAvgOne -1
+        }else if(data.saleCattleWeight >= 500 && data.saleCattleWeight < 550){
+          data.carcassPercentAvg  = infoOne.value.carcassPercentAvgOne -3
+          data.meatPercentAvg  = infoOne.value.meatPercentAvgOne -3
+        }else if(data.saleCattleWeight >= 400 && data.saleCattleWeight < 500){
+          data.carcassPercentAvg  = infoOne.value.carcassPercentAvgOne -4.5
+          data.meatPercentAvg  = infoOne.value.meatPercentAvgOne -4.5
+        }else if(data.saleCattleWeight < 400){
+          data.carcassPercentAvg  = infoOne.value.carcassPercentAvgOne -5
+          data.meatPercentAvg  = infoOne.value.meatPercentAvgOne -5
+        }
+      }
+      if(infoXs.cowWeight != ''){
+        infoXs.dtWeight = infoXs.cowWeight * (Number(data.carcassPercentAvg)/100)//胴体重
+        infoXs.beefWeight = infoXs.cowWeight * (Number(data.meatPercentAvg)/100)//净肉重
+      }
+      if(infoXs.dtRate == 0){
+        infoXs.dtincome = ((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoXs.transporFee
+        infoXs.dtsalesProfit = 0
+      }else{
+        infoXs.dtincome = (((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoXs.transporFee)*(1+infoXs.dtRate/100)
+        infoXs.dtsalesProfit = infoXs.dtincome * infoXs.dtRate/100
+      }
+      infoXs.dtprice = (infoXs.dtincome*10000)/infoXs.dtWeight
+      if(infoXs.beefRate == 0){
+        infoXs.beefincome = ((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoXs.transporFee
+        infoXs.beefsalesProfit = 0
+      }else{
+        infoXs.beefincome = (((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoXs.transporFee)*(1+infoXs.beefRate/100)
+        infoXs.beefsalesProfit = infoXs.beefincome * infoXs.beefRate/100
+      }
+      infoXs.beefprice = (infoXs.beefincome*10000)/infoXs.beefWeight
+      arrTwo.value.push(infoXs)
+      // 计算牛只销售--胴体销售
+      let infoDt = {cowrate:0,beefRate:0,price:0,income:0,salesProfit:0,rate:0}
+      infoDt.transporFee = infoXs.transporFee
+      if(data.saleCarcassCutPrice && data.saleCarcassCutPrice != ''){
+        infoDt.butcherFee = (Number(data.saleCarcassCutPrice) * Number(data.cattleNum))/10000
+      }else{
+        infoDt.butcherFee = 0
+      }
+      infoDt.dtWeight = infoXs.dtWeight
+      if(data.saleCarcassPrice && data.saleCarcassPrice != ''){
+        infoDt.price = Number(data.saleCarcassPrice)  //销售单价
+        infoDt.income = (infoDt.dtWeight * infoDt.price)/10000
+        //销售毛利(万元)
+        infoDt.salesProfit = infoDt.income-((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)-newobj.invest-infoDt.transporFee-infoDt.butcherFee
+        infoDt.rate = (infoDt.salesProfit/infoDt.income)*100  //毛利率
+      }
+      //活牛重
+      if(data.saleCattleWeight != ''){
+        infoDt.cowWeight = Number(data.cattleNum)*Number(data.saleCattleWeight)
+      }
+      if(infoDt.cowrate == 0){
+        infoDt.cowincome = ((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoDt.transporFee
+        infoDt.cowsalesProfit = 0
+      }else{
+        infoDt.cowincome = (((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoDt.transporFee)*(1+infoDt.dtRate/100)
+        infoDt.cowsalesProfit = infoDt.dtincome * infoDt.dtRate/100
+      }
+      infoDt.cowprice = (infoDt.cowincome*10000)/infoDt.cowWeight
+      //净肉重
+      infoDt.beefWeight = infoXs.beefWeight
+      if(infoDt.beefRate == 0){
+        infoDt.beefincome = ((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoDt.transporFee
+        infoDt.beefsalesProfit = 0
+      }else{
+        infoDt.beefincome = (((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoDt.transporFee)*(1+infoDt.beefRate/100)
+        infoDt.beefsalesProfit = infoDt.beefincome * infoDt.beefRate/100
+      }
+      infoDt.beefprice = (infoDt.beefincome*10000)/infoDt.beefWeight
+      arrThree.value.push(infoDt)
+      // 计算牛只销售--牛肉销售
+      let infoBeef = {cowrate:0,dtRate:0,beefprice:0,beefincome:0,beefsalesProfit:0,beefRate:0}
+      infoBeef.transporFee = infoXs.transporFee
+      if(data.saleBeefCutPrice && data.saleBeefCutPrice != ''){
+        infoBeef.butcherFee = (Number(data.saleBeefCutPrice) * Number(data.cattleNum))/10000
+      }else{
+        infoBeef.butcherFee = 0
+      }
+      infoBeef.beefWeight = infoXs.beefWeight
+      if(data.saleBeefPrice && data.saleBeefPrice !=''){
+        infoBeef.beefprice = Number(data.saleBeefPrice)  //销售单价
+        infoBeef.beefincome = (infoBeef.beefWeight * infoBeef.beefprice)/10000
+        infoBeef.beefsalesProfit = infoBeef.beefincome-((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)-newobj.invest-infoBeef.transporFee-infoBeef.butcherFee//销售毛利(万元)
+        infoBeef.beefRate = (infoBeef.beefsalesProfit/infoBeef.beefincome)*100  //毛利率
+      }
+      //活牛重
+      if(data.saleCattleWeight != ''){
+        infoBeef.cowWeight = Number(data.cattleNum)*Number(data.saleCattleWeight)
+      }
+      if(infoBeef.cowrate == 0){
+        infoBeef.cowincome = ((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoBeef.transporFee
+        infoBeef.cowsalesProfit = 0
+      }else{
+        infoBeef.cowincome = (((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoBeef.transporFee)*(1+infoBeef.cowrate/100)
+        infoBeef.cowsalesProfit = infoBeef.dtincome * infoBeef.cowrate/100
+      }
+      infoBeef.cowprice = (infoBeef.cowincome*10000)/infoBeef.cowWeight
+      //胴体重
+      infoBeef.dtWeight = infoXs.dtWeight
+      if(infoBeef.dtRate == 0){
+        infoBeef.dtincome = ((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoBeef.transporFee
+        infoBeef.dtsalesProfit = 0
+      }else{
+        infoBeef.dtincome = (((Number(data.cattleNum)*Number(data.purchaseWeight)*Number(data.purchasePrice))/10000)+newobj.invest+infoBeef.transporFee)*(1+infoBeef.dtRate/100)
+        infoBeef.dtsalesProfit = infoBeef.dtincome * infoBeef.dtRate/100
+      }
+      infoBeef.dtprice = (infoBeef.dtincome*10000)/infoBeef.dtWeight
+      arrFour.value.push(infoBeef)
+      //计算牛副
+      if(data.viceCowhidePrice && data.viceCowhidePrice != '' && data.saleCattleWeight != ''){
+        infoVice.value.viceCowhideMoney = data.saleCattleWeight * (Number(infoOne.value.viceCowhide)/100) * Number(data.viceCowhidePrice)
+      }
+      if(data.viceOilPrice && data.viceOilPrice != '' && data.saleCattleWeight != ''){
+        infoVice.value.viceOilMoney = data.saleCattleWeight * (Number(infoOne.value.viceOil)/100) * Number(data.viceOilPrice)
+      }
+      if(data.viceHeadPrice && data.viceHeadPrice != '' && data.saleCattleWeight != ''){
+        infoVice.value.viceHeadMoney = data.saleCattleWeight * (Number(infoOne.value.viceHead)/100) * Number(data.viceHeadPrice)
+      }
+      if(data.viceHoofPrice && data.viceHoofPrice != '' && data.saleCattleWeight != ''){
+        infoVice.value.viceHoofMoney = data.saleCattleWeight * (Number(infoOne.value.viceHoof)/100) * Number(data.viceHoofPrice)
+      }
+      if(data.viceOmasumPrice && data.viceOmasumPrice != '' && data.saleCattleWeight != ''){
+        infoVice.value.viceOmasumMoney = data.saleCattleWeight * (Number(infoOne.value.viceOmasum)/100) * Number(data.viceOmasumPrice)
+      }
+      if(data.viceLiverPrice && data.viceLiverPrice != '' && data.saleCattleWeight != ''){
+        infoVice.value.viceLiverMoney = data.saleCattleWeight * (Number(infoOne.value.viceLiver)/100) * Number(data.viceLiverPrice)
+      }
+      if(data.viceIntestinePrice && data.viceIntestinePrice != '' && data.saleCattleWeight != ''){
+        infoVice.value.viceIntestineMoney = data.saleCattleWeight * (Number(infoOne.value.viceIntestine)/100) * Number(data.viceIntestinePrice)
+      }
+      if(data.viceLungPrice && data.viceLungPrice != '' && data.saleCattleWeight != ''){
+        infoVice.value.viceLungMoney = data.saleCattleWeight * (Number(infoOne.value.viceLung)/100) * Number(data.viceLungPrice)
+      }
+      if(data.viceTripePrice && data.viceTripePrice != '' && data.saleCattleWeight != ''){
+        infoVice.value.viceTripeMoney = data.saleCattleWeight * (Number(infoOne.value.viceTripe)/100) * Number(data.viceTripePrice)
+      }
+      if(data.viceHeratPrice && data.viceHeratPrice != '' && data.saleCattleWeight != ''){
+        infoVice.value.viceHeratMoney = data.saleCattleWeight * (Number(infoOne.value.viceHeart)/100) * Number(data.viceHeratPrice)
+      }
+      infoVice.value.totalWeight = Number(infoOne.value.viceCowhide) + Number(infoOne.value.viceOil) + Number(infoOne.value.viceHead) + Number(infoOne.value.viceHoof)+Number(infoOne.value.viceOmasum)+
+                              Number(infoOne.value.viceLiver)+Number(infoOne.value.viceIntestine)+Number(infoOne.value.viceLung)+Number(infoOne.value.viceTripe)+Number(infoOne.value.viceHeart)
+      infoVice.value.totalMoney = infoVice.value.viceCowhideMoney+infoVice.value.viceOilMoney+infoVice.value.viceHeadMoney+
+                                  infoVice.value.viceHoofMoney+infoVice.value.viceOmasumMoney+
+                                  infoVice.value.viceLiverMoney +infoVice.value.viceIntestineMoney+infoVice.value.viceLungMoney+
+                                  infoVice.value.viceTripeMoney+infoVice.value.viceHeratMoney
+    }
+    
+    let infoOne = ref({})
+    //获取当前牛种信息
+    async function getcattleData(cattleBreed,cattleType){
+      await item.getCattleParamOne({cattleBreed:cattleBreed,cattleType:cattleType}).then(res =>{
+        if (res.code == 101) {
+          infoOne.value.carcassPercentAvgOne = res.data && res.data.carcassPercentAvg?res.data.carcassPercentAvg:0
+          infoOne.value.meatPercentAvgOne = res.data && res.data.meatPercentAvg?res.data.meatPercentAvg:0
+          infoOne.value.viceCowhide = res.data && res.data.viceCowhide?res.data.viceCowhide:0
+          infoOne.value.viceHead = res.data && res.data.viceHead?res.data.viceHead:0
+          infoOne.value.viceHeart = res.data && res.data.viceHeart?res.data.viceHeart:0
+          infoOne.value.viceHoof = res.data && res.data.viceHoof?res.data.viceHoof:0
+          infoOne.value.viceIntestine = res.data && res.data.viceIntestine?res.data.viceIntestine:0
+          infoOne.value.viceLiver = res.data && res.data.viceLiver?res.data.viceLiver:0
+          infoOne.value.viceLung = res.data && res.data.viceLung?res.data.viceLung:0
+          infoOne.value.viceOil = res.data && res.data.viceOil?res.data.viceOil:0
+          infoOne.value.viceOmasum = res.data && res.data.viceOmasum?res.data.viceOmasum:0
+          infoOne.value.viceTripe = res.data && res.data.viceTripe?res.data.viceTripe:0
+        }
+      })
+    }
+    onMounted(()=>{
+      getData() 
+    })
+
+    return {
+      typesearchFilter,getData,tabData,changePage,currentPage,moment,lookDetail,info,contentTypeModel,arrOne,arrTwo,arrThree,arrFour,infoOne,infoVice
+    }
+  },
+  methods:{
+    
+  }
+})
+</script>
+
+<style lang="scss">
+.recondsInfo{
+  padding: 1em;
+  .page_style{
+    text-align: right; margin-top: 1em;
+    background-color: var(--system-container-background);
+  }
+}
+.el-divider__text.is-center{
+  color: #409EFF !important;
+}
+.rowStyle{
+  height: 30px;
+  text-align: center;
+  line-height: 30px;
+}
+</style>

+ 132 - 0
src/views/cattleTrdeValue/referenceInfo.vue

@@ -0,0 +1,132 @@
+<template>
+  <div class="referenceInfo">
+    <Form ref="optClassifyFormRef" :model="optClassifyForm" :label-width="100">
+      <Form-item label="上传图片" prop="filePath">
+        <Upload
+          ref="upload"
+          type="drag"
+          :action="uploadUrl"
+          :headers="{'X-AIYANGNIU-SIGNATURE':token}"
+          :show-upload-list="false"
+          :on-success="handleSuccess"
+          :before-upload="beforeUpload"
+          :format="['jpg','jpeg','png']"
+          :max-size="2048"
+          :on-format-error="handleFormatError"
+          class="upload_outer_img">
+          <div v-if="optClassifyForm.filePath != ''"> <img :src="optClassifyForm.filePath" width="100%" alt=""></div>
+          <div v-else class="upload_border">
+            <p><Icon type="md-cloud-upload" size="35" color="#2db7f5" /></p>
+            <p>点击上传图片</p>
+          </div>
+        </Upload>
+        <div v-show="optClassifyForm.filePath" style="color:#2db7f5; cursor: pointer;" @click="gotoCheckImg(optClassifyForm.filePath)">
+          <i class="iconfont icon-htmal5icon09"></i> 查看大图
+        </div>
+        <div style="color:#aaa;font-size:12px;">图片格式为 jpg、jpeg、png</div>
+      </Form-item>
+      <Form-item label="">
+        <Button type="primary" @click="submitOptClassify">确定提交</Button>
+      </Form-item>
+    </Form>
+  </div>
+</template>
+
+<script>
+import { defineComponent,ref,reactive,onMounted } from 'vue'
+import {item} from '@/request/api'
+import { ElMessage } from 'element-plus'
+import api from '@/request/apiConfig' //真实接口配置
+import {compressFileM} from '@/utils/system/uploadCompress.js'
+export default defineComponent({
+  components:{
+    
+  },
+  setup() {
+    let optClassifyForm = reactive({filePath:''})
+    const token = localStorage.aynUserToken
+    const uploadUrl = api.apiManager + '/manager/images/upload'
+    function beforeUpload(file){
+      // 进行2.5倍系数压缩
+      return (compressFileM(file,2.5))
+    }
+
+    function handleFormatError (file) {
+      ElNotification.warning({
+        title: '温馨提示',
+        message: '文件格式不正确',
+        showClose: false,
+      })
+    }
+
+    function handleSuccess(res, file, fileList){
+      if(res.code == '101'){
+        optClassifyForm.filePath = res.data
+      }
+    }
+
+    // 打开新页面查看照片
+    function gotoCheckImg(pic){
+      window.open(pic,'blank')
+    }
+
+    //获取列表内容
+    async function getData(){
+      await item.getCattleFatness().then(res =>{
+        if (res.code == 101) {
+          optClassifyForm.filePath = res.data?res.data:''
+        }
+      })
+    }
+    // 提交操作分类
+    function submitOptClassify(){
+      item.insertUrl({imgUrl:optClassifyForm.filePath}).then(res=>{
+        if(res.code === 101){
+          ElMessage({
+            message: res.message || '操作成功!',
+            type: 'success',
+          })
+          getData()
+        }else{
+          ElMessage({
+            message: res.message || '操作失败!',
+            type: 'warning',
+          })
+        }
+      })
+    }
+
+    onMounted(()=>{
+      getData() 
+    })
+
+    return {
+      optClassifyForm,uploadUrl,token,
+      getData,handleFormatError,handleSuccess,beforeUpload,gotoCheckImg,submitOptClassify
+    }
+  },
+  methods:{
+    
+  }
+
+})
+</script>
+
+<style lang="scss" scoped>
+.referenceInfo{
+  padding: 1em;
+  .page_style{
+    text-align: right; margin-top: 1em;
+    background-color: var(--system-container-background);
+  }
+  .upload_outer_img{
+    max-width: 10%;
+    img{
+      width: 100%; padding: 0;
+    }
+    .upload_border{
+      width: 100%; padding: 3em;
+    }
+  }
+}
+</style>

+ 315 - 0
src/views/classifyManage/classifyManageIndex.vue

@@ -0,0 +1,315 @@
+/**
+  // author:zhangb
+  // time:2022-11-07
+  // desc:牛只分类管理
+*/
+<template>
+  <div class="classifyManage">
+    <!-- top -->
+    <div>
+      <Row>
+        <Col span="24" style="text-align:right;">
+          <Button type="primary" @click="addNewClassify">添加一级分类</Button>
+        </Col>
+      </Row>
+    </div>
+
+    <!-- tree content -->
+    <div class="tree_content">
+      <el-tree :data="dataSource" show-checkbox node-key="typeId" :props="classifyTreePropsSet" :expand-on-click-node="false" style="width:90%;">
+        <template #default="{ node, data }">
+          <div class="custom-tree-node">
+            <span>{{ node.data.name }}</span>
+            <span>输出顺序:{{node.data.sort || '暂无'}}</span>
+            <span style="text-align:right !important;">
+              <a v-show="node.level < 3" @click="addNextClassify(node,data)">添加下级分类</a>
+              <a v-show="node.level === 3" @click="optClassifyImg(node,data)">分类图片</a>
+              <a style="margin-left: 8px" @click="editTreeClassify(node,data)">编辑</a>
+              <a style="margin-left: 8px" @click="delClassidy(node,data)">删除</a>
+            </span>
+          </div>
+        </template>
+      </el-tree>
+    </div>
+
+    <!-- 新增分类信息 -->
+    <Modal v-model="optClassifyModal" :title="optClassifyModalTitle" width="30%">
+      <Tag v-show="optClassifyModalTitle == '添加下级分类'" color="primary" size="medium" style="margin-bottom:1em;">
+        当前上级:{{recordClassifyData.name}}
+      </Tag>
+      <Form ref="optClassifyFormRef" :model="optClassifyForm" :rules="optClassifyRule" :label-width="100">
+        <FormItem prop="name" label="分类名称:">
+          <Input v-model="optClassifyForm.name" placeholder="新增分类名称" style="width:80%;"></Input>
+        </FormItem>
+        <FormItem prop="sort" label="输出排序:">
+          <InputNumber :min="1" v-model="optClassifyForm.sort" placeholder="请输入输出顺序" style="width:80%;" />
+        </FormItem>
+      </Form>
+      <template #footer>
+        <div style="text-align:center;">
+          <Button style="margin-right:5em;" @click="optClassifyModal = false">取消操作</Button>
+          <Button type="primary" @click="submitOptClassify">确定提交</Button>
+        </div>
+      </template>
+    </Modal>
+
+    <!-- 三级分类显示图片 -->
+    <Modal v-model="optClassifyImgModal" :title="optClassifyModalTitle" width="30%">
+      <Tag color="primary" size="medium" style="margin-bottom:1em;">
+        当前分类:{{recordClassifyData.name}}
+      </Tag>
+
+      <div style="margin-left:1rem;">
+        <div style="margin:.5rem 0;">分类图片</div>
+        <Upload
+          type="drag"
+          :action="uploadUrl"
+          :headers="{'X-AIYANGNIU-SIGNATURE':token}"
+          :show-upload-list="false"
+          :on-success="handleSuccess"
+          :before-upload="handleBeforeUpload"
+          :format="['jpg','jpeg','png']"
+          :max-size="2048"
+          :on-format-error="handleFormatError"
+          style="width:100px; height:100px;">
+          <div v-if="!recordClassifyData.picUrl" style="width:100px; height:100px; line-height:100px; font-size:30px; color:#aaa;">
+            <span>+</span>
+          </div>
+          <div v-else style="width:100px; height:100px;"><img :src="recordClassifyData.picUrl" style="width:100%; height:100%;" alt=""></div>
+        </Upload>
+      </div>
+      
+      <template #footer>
+        <div style="text-align:center;">
+          <Button style="margin-right:5em;" @click="optClassifyImgModal = false">取消操作</Button>
+          <Button type="primary" @click="confirmSubClassifyImg">确定提交</Button>
+        </div>
+      </template>
+    </Modal>
+
+  </div>
+</template>
+
+<script>
+import { Message,Modal } from 'view-ui-plus'
+import { defineComponent,reactive,ref,getCurrentInstance, onMounted} from 'vue'
+import { useRouter,useRoute} from 'vue-router'
+import {compressFileM} from '@/utils/system/uploadCompress.js'
+import {cow} from '@/request/api'
+import api from '@/request/apiConfig' //真实接口配置
+export default defineComponent({
+  components:{
+    
+  },
+  setup() {
+    let {proxy} = getCurrentInstance();
+    const route = useRoute()
+    const router = useRouter()
+
+    const token = localStorage.aynUserToken
+    const uploadUrl = api.apiManager + '/manager/images/upload'
+
+    let dataSource = ref([])
+    const classifyTreePropsSet = {label:'name'}
+
+    let optClassifyModal = ref(false)
+    let optClassifyModalTitle = ref('新增分类')
+    let optClassifyForm = reactive({name:'',sort:null,reqRandom:''})
+    let optClassifyRule = ref({
+      name: [{ required: true, message: '请填写分类名称', trigger: 'blur' }],
+      sort: [{ required: true, type:'number', message: '请填写分类输出顺序', trigger: 'blur' }],
+    })
+
+    // 获取全部分类内容
+    async function getAllClassifyData(){
+      await cow.getCattleClassify({parentId:0}).then(res=>{
+        if(res.code === 101){
+          dataSource.value = res.data || []
+        }
+      })
+    }
+
+    // 新增一级分类
+    function addNewClassify(){
+      optClassifyForm.name = '',optClassifyForm.sort = ''
+      optClassifyModal.value = true
+      optClassifyModalTitle.value = '新增分类'
+    }
+
+    let recordClassifyData = ref({})  //当前操作分类
+    // 编辑数节点
+    function editTreeClassify(n,d){
+      recordClassifyData.value = JSON.parse(JSON.stringify(d))
+      optClassifyForm.name = d.name,optClassifyForm.sort = d.sort
+      optClassifyModal.value = true
+      optClassifyModalTitle.value = '编辑分类'
+    }
+
+    // 添加下级分类
+    function addNextClassify(n,d){
+      optClassifyForm.name = '',optClassifyForm.sort = ''
+      recordClassifyData.value = JSON.parse(JSON.stringify(d))
+      optClassifyModal.value = true
+      optClassifyModalTitle.value = '添加下级分类'
+      console.log('数据',recordClassifyData);
+    }
+
+    // 提交操作分类
+    function submitOptClassify(){
+      proxy.$refs['optClassifyFormRef'].validate(async (valid) => {
+        if (valid) {
+          let sendParams = JSON.parse(JSON.stringify(optClassifyForm))
+          let repFun = ''
+          // 新增一级分类
+          if(optClassifyModalTitle.value === '新增分类'){
+            sendParams.parentId = "0"
+            repFun = cow.addCowClassify
+          }
+          // 添加下级分类
+          else if(optClassifyModalTitle.value === '添加下级分类'){
+            sendParams.parentId = recordClassifyData.value.id
+            repFun = cow.addCowClassify
+          }
+          // 编辑分类
+          else if(optClassifyModalTitle.value === '编辑分类'){
+            console.log(recordClassifyData.value);
+            sendParams = JSON.parse(JSON.stringify(recordClassifyData.value))
+            sendParams.name = optClassifyForm.name
+            sendParams.sort = optClassifyForm.sort
+            repFun = cow.updateCowClassify
+          }
+          await repFun(sendParams).then(res=>{
+            if(res.code === 101){
+              optClassifyModal.value = false
+              Message.success({ content: res.retMsg || '操作成功'})
+              getAllClassifyData()
+            }else{
+              Message.error({content: res.retMsg || '操作失败'})
+            }
+          }).catch(e=>{
+            Message.error({content: e.retMsg || '操作失败'})
+          })
+        } else {
+          Message.warning({content: '请填写完整内容'})
+        }
+      })
+    }
+
+    // 删除分类
+    function delClassidy(n,d){
+      Modal.confirm({
+        title: '温馨提示',
+        content: `<p>您确定要删除 <b>${d.name}</b> 分类吗?</p>`,
+        onOk:async () => {
+          cow.deleteCowClassify({ids:d.id}).then(res=>{
+            if(res.code === 101){
+              Message.success(res.retMsg || '操作成功');
+              getAllClassifyData()
+            }else{
+              Message.error({content: res.msg || '操作失败'})
+            }
+          }).catch(e=>{
+            Message.error({content: e.msg || '操作失败'})
+          })
+        },
+        onCancel: () => {
+          Message.info('您已取消操作');
+        }
+      });
+    }
+
+    let optClassifyImgModal = ref(false)
+    // 操作分类图片
+    function optClassifyImg(n,d){
+      recordClassifyData.value = JSON.parse(JSON.stringify(d))
+      optClassifyImgModal.value = true
+    }
+
+    function handleBeforeUpload(file){
+      // 进行2.5倍系数压缩
+      return (compressFileM(file,2.5))
+    }
+
+    function handleSuccess (res, file) {
+      if(res.code === 101){
+        recordClassifyData.value.picUrl = res.data
+      }
+      console.log('结果输出',recordClassifyData.value);
+    }
+
+    function handleFormatError (file) {
+      this.$Notice.warning({
+          title: '温馨提示',
+          desc: '上传失败!'
+      });
+    }
+
+    async function confirmSubClassifyImg(){
+      let sendParams = JSON.parse(JSON.stringify(recordClassifyData.value))
+      await cow.updateCowClassify(sendParams).then(res=>{
+        if(res.code === 101){
+          Message.success(res.retMsg || '操作成功');
+          optClassifyImgModal.value = false
+          getAllClassifyData()
+        }else{
+          Message.error({content: res.msg || '操作失败'})
+        }
+      }).catch(e=>{
+        Message.error({content: e.msg || '操作失败'})
+      })
+    }
+    
+    // 初始化挂载
+    onMounted(async ()=>{
+      getAllClassifyData() //获取分类树
+    })
+
+    return {
+      dataSource,optClassifyModal,optClassifyModalTitle,optClassifyForm,optClassifyRule,recordClassifyData,
+      classifyTreePropsSet,optClassifyImgModal,uploadUrl,token,
+      editTreeClassify,addNewClassify,submitOptClassify,addNextClassify,delClassidy,optClassifyImg,handleSuccess,
+      handleFormatError,handleBeforeUpload,confirmSubClassifyImg
+    }
+  },
+  methods: {
+    
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.classifyManage{
+  padding: 2em;
+  .tree_content{
+    background-color: #fff;
+    padding: 2em; margin: 2em 0;
+  }  
+  .upload_classify_img{
+    .upload_border{
+      width: 100%; height: 100%;
+    }
+  }
+}
+</style>
+<style lang="scss">
+.classifyManage{
+  .el-tree-node__label{
+    border: 1px solid #000;
+    width: 100%;
+  }
+  .custom-tree-node {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-size: 14px;
+    padding-right: 8px;
+    span{
+      width: 33%;
+    }
+  }
+  .ivu-upload-drag{
+    border: 0px;
+  }
+}
+</style>

+ 157 - 0
src/views/contentManage/contentList.vue

@@ -0,0 +1,157 @@
+<template>
+  <div class="contentList">
+    <Select v-model="typesearchFilter.contentTypeCode" clearable style="width:260px" placeholder="请选择内容类型" @on-change="getData">
+      <Option v-for="item in typeList" :value="item.code" :key="item.id">{{ item.name }}</Option>
+    </Select>
+    <el-button type="primary" style="float:right;" @click="$router.push('newContent')">新增内容</el-button>
+
+    <el-table :data="tabData.data" style="width: 100%; margin-top:2em;" border>
+      <el-table-column label="序号" type="index" width="70" align="center"></el-table-column>
+      <el-table-column prop="contentTypeName" label="类别名称" width="250"> </el-table-column>
+      <el-table-column label="标题名称" width="400">
+        <template #default="scope">
+          {{scope.row.name||'无'}}
+        </template>
+      </el-table-column>
+      <el-table-column label="首页展示" width="120" align="center">
+        <template #default="scope"> 
+         {{scope.row.isShow}}
+        </template>
+      </el-table-column>
+      <el-table-column prop="showOrder" label="顺序号" width="120">
+        <template #default="scope"> 
+         {{scope.row.showOrder || '未排序'}}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作">
+        <template #default="scope">
+          <!--按钮-->
+          <el-button type="primary" size="small" @click="$router.push('editContent?id='+scope.row.id)">编辑</el-button>&nbsp;
+          <el-button type="danger" size="small" @click="delContent(scope.row.id)">删除</el-button>&nbsp;
+          <el-button type="warning" v-if="scope.row.status=='2'&&scope.row.contypeId=='00'" size="small" @click.native="auditContent(scope.row.id)">审核</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    
+    <div class="page_style">
+      <Page :total="tabData.total" :model-value="currentPage" show-elevator show-total @on-change="changePage" />
+    </div>
+    
+
+  </div>
+</template>
+
+<script>
+import { defineComponent,ref,reactive,onMounted } from 'vue'
+import {content} from '@/request/api'
+import { ElMessage } from 'element-plus'
+export default defineComponent({
+  components:{
+    
+  },
+  setup() {
+    
+    let typeList = ref([])
+    let typesearchFilter = reactive({limit:10,offset:0,contentTypeCode:''})
+
+    // 获取分类列表信息
+    async function initData () {
+      await content.GetContentTypeList({limit:1000,offset:0}).then((res) => {
+        if (res.code =='101') {
+          typeList.value = res.data || []
+        }
+      })
+    }
+
+    let tabData = ref({})
+    
+    //获取列表内容
+    async function getData(){
+      await content.GetContentList(typesearchFilter).then(res =>{
+        if (res.code == 101) {
+          tabData.value = res || {}
+        }
+      })
+    }
+
+    let currentPage = ref(1)
+    //更改页码
+    function changePage (page) {
+      if(tabData.value.limit){
+        typesearchFilter.offset = (page -1) *  tabData.value.limit  //更新偏移量
+        currentPage = page  //切换当前页码
+        getData()       //获取广告数据
+      }
+    }
+
+    onMounted(()=>{
+      initData()
+      getData() 
+    })
+
+    return {
+      typeList,
+      typesearchFilter,
+      getData,tabData,changePage,currentPage
+    }
+  },
+  methods:{
+    // 删除内容
+    delContent(id){
+      this.$Modal.confirm({
+        title: '温馨提示',
+        content: '您确定删除当前内容吗?',
+        onOk:async () => {
+          await content.DeleteContent({id:id}).then(res=>{
+            if(res.code == 101){
+              ElMessage({
+                message: res.message || '操作成功!',
+                type: 'success',
+              })
+              this.changePage(1)  //重新获取列表
+            }else{
+              ElMessage({
+                message: res.message || '操作失败!',
+                type: 'warning',
+              })
+            }
+          }).catch(e=>{
+            ElMessage({
+              message: e.message || '添加失败!',
+              type: 'error',
+            })
+          })
+        },
+        onCancel: () => {
+          this.$Message.info('您已取消操作!');
+        }
+      });
+    },
+
+    //审核内容
+    async auditContent(id) {
+        await content.AuditContent({id}).then((res) => {
+          if(res.code == '101') {
+            this.$Message.success(res.message)
+            this.changePage(1)  //重新获取列表
+          }else {
+            this.$Message.error(res.message)
+          }
+        }).catch((err) => {
+          this.$Message.error(err)
+        })
+    },
+  }
+
+})
+</script>
+
+<style lang="scss" scoped>
+.contentList{
+  padding: 1em;
+  .page_style{
+    text-align: right; margin-top: 1em;
+    background-color: var(--system-container-background);
+  }
+}
+</style>

+ 211 - 0
src/views/contentManage/contentTypeList.vue

@@ -0,0 +1,211 @@
+<template>
+  <div class="contentTypeList">
+    <div style="text-align:right;">
+      <el-button type="primary" @click="addNewType">新增类别</el-button>&nbsp;&nbsp;&nbsp;
+      <el-button type="warning" @click="delConType('MULTI')">删除所选</el-button>
+    </div>
+    <el-table ref="contentTypeListTab" :data="typeListData.data" style="width: 100%; margin-top:1.5em;" border>
+      <el-table-column type="selection" width="50" align="center"></el-table-column>
+      <el-table-column label="序号" type="index" width="65" align="center"></el-table-column>
+      <el-table-column label="分类名称" width="450">
+        <template #default="scope">
+          <span>
+            {{scope.row.name || '无'}}
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="code" label="分类编码" width="250"></el-table-column>
+      <el-table-column label="操作">
+        <template #default="scope">
+          <el-button type="primary" size="small" @click="editConType(scope.row)">编辑</el-button>&nbsp;
+          <el-button type="danger" size="small" @click="delConType(scope.row.id)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    
+    <div class="page_style">
+      <Page :total="typeListData.total" :model-value="currentPage" show-elevator show-total @on-change="changePage" />
+    </div>
+    
+    <!-- 编辑弹出层 -->
+    <Modal v-model="contentTypeModel" title="内容类别" ok-text="确定">
+      <Form ref="contentType" :model="contentRowData" :rules="changeRules" :label-width="110" >
+        <Form-item label="类别名称:" prop="name">
+          <Input v-model="contentRowData.name" placeholder="请输入类别名称" style="width:90%;"></Input>
+        </Form-item>
+        <Form-item label="类别编码:" prop="code">
+          <Input v-model="contentRowData.code" placeholder="请输入类别编码" style="width:90%;"></Input>
+        </Form-item>
+      </Form>
+      <template #footer>
+        <div style="text-align:center;">
+          <el-button style="margin-right:3em;" @click="contentTypeModel = false">取消操作</el-button>
+          <el-button type="primary" @click="confirmSub">确定提交</el-button>
+        </div>
+      </template>
+    </Modal> 
+
+  </div>
+</template>
+
+<script>
+import { defineComponent,ref,reactive,onMounted,getCurrentInstance } from 'vue'
+import {content} from '@/request/api'
+import { ElMessage } from 'element-plus'
+export default defineComponent({
+  components:{
+    
+  },
+  setup() {
+    let {proxy} = getCurrentInstance();
+    let pageParams = reactive({limit:10,offset:0})
+    let typeListData = ref({})
+
+    async function getData(){
+      await content.GetContentTypeList(pageParams).then(res =>{
+        if (res.code == 101) {
+          typeListData.value = res || {}
+        }
+      })
+    }
+    
+    let currentPage = ref(1)
+
+    //更改页码
+    function changePage (page) {
+      if(typeListData.value.limit){
+        pageParams.offset = (page -1) *  typeListData.value.limit  //更新偏移量
+        currentPage.value = page  //切换当前页码
+        getData()       //获取广告数据
+      }
+    }
+
+    let contentTypeModel = ref(false)
+    let contentRowData = reactive({name:'',code:'',id:''})
+    // 表单验证项
+    let changeRules = ref({
+      name: [{ required: true, message: '请填写类别名称', trigger: 'blur' }],
+      code: [{ required: true, message: '请输入类型编码', trigger: 'blur' }],
+    })
+    // 确定提交表单  
+    function confirmSub(){
+      let reqFn = null
+      if(contentRowData.id){
+        reqFn = content.UpdateConType
+      }else{
+        reqFn = content.AddNewConType
+      }
+      proxy.$refs['contentType'].validate(async (valid) => {
+        if (valid) {
+          await reqFn(contentRowData).then(res=>{
+            if(res.code == 101){
+              contentTypeModel.value = false
+              ElMessage({
+                message: res.message || '添加成功!',
+                type: 'success',
+              })
+              changePage(1)  //重新获取列表
+            }else{
+              ElMessage({
+                message: res.message || '添加失败!',
+                type: 'warning',
+              })
+            }
+          }).catch(e=>{
+            ElMessage({
+              message: e.message || '添加失败!',
+              type: 'error',
+            })
+          })
+        } else {
+          ElMessage({
+            message:'请填写完表单数据!',
+            type: 'warning',
+          })
+        }
+      })
+    }
+
+    // 编辑内容
+    function editConType(row) {
+      contentTypeModel.value = true
+      let passVal = JSON.parse(JSON.stringify(row))
+      contentRowData.code = passVal.code
+      contentRowData.name = passVal.name
+      contentRowData.id = passVal.id
+    }
+
+    // 新增分类
+    function addNewType(){
+      contentRowData.code = ''
+      contentRowData.name = ''
+      contentRowData.id = ''
+      contentTypeModel.value = true
+    }
+
+    onMounted(()=>{
+      getData() 
+    })
+
+    return {
+      typeListData,
+      getData,changePage,currentPage,
+      editConType,contentTypeModel,contentRowData,changeRules,confirmSub,addNewType
+    }
+  },
+
+  methods:{
+    // 删除分类
+    delConType(id){
+      let params = {}
+      if(id == 'MULTI'){
+        let selection = this.$refs.contentTypeListTab.getSelectionRows(),
+        ids = selection.map(val => val.id).join(',')
+        params = {ids: ids}
+      }else{
+        params = {ids: id}
+      }
+
+      this.$Modal.confirm({
+        title: '温馨提示',
+        content: '您确定删除选中分类吗?',
+        onOk:async () => {
+          await content.DeleteConType(params).then(res=>{
+            if(res.code == 101){
+              ElMessage({
+                message: res.message || '操作成功!',
+                type: 'success',
+              })
+              this.changePage(1)  //重新获取列表
+            }else{
+              ElMessage({
+                message: res.message || '操作失败!',
+                type: 'warning',
+              })
+            }
+          }).catch(e=>{
+            ElMessage({
+              message: e.message || '添加失败!',
+              type: 'error',
+            })
+          })
+        },
+        onCancel: () => {
+          this.$Message.info('您已取消操作!');
+        }
+      });
+    }
+  }
+
+})
+</script>
+
+<style lang="scss" scoped>
+.contentTypeList{
+  padding: 1em;
+  .page_style{
+    text-align: right; margin-top: 1em;
+    background-color: var(--system-container-background);
+  }
+}
+</style>

+ 249 - 0
src/views/contentManage/newContent.vue

@@ -0,0 +1,249 @@
+<template>
+  <div class="newContent">
+    <div>
+      <Form ref="contentForm" :model="newContentForm" :rules="newContentRule" :label-width="120">
+        <Form-item label="牛只分类" prop="contypeId">
+          <Select v-model="newContentForm.contypeId" :label-in-value="true" placeholder="请选择所属分类" style="width:480px">
+            <Option v-for="type in typeDataList" :key="type.id" :value="type.code" :label="type.name">{{type.name}}</Option>
+          </Select>
+        </Form-item>
+        <Form-item label="新闻类型" prop="newsType">
+          
+          <Select v-model="newContentForm.newsType" placeholder="请选择新闻类型" style="width:480px">
+            <Option value="新闻类型">新闻类型</Option>
+            <Option value="平台动态">平台动态</Option>
+            <Option value="运营资讯">运营资讯</Option>
+            <Option value="媒体报道">媒体报道</Option>
+          </Select>
+        </Form-item>
+        <Form-item label="发布时间" prop="releaseTime">
+          <Date-picker type="date" placeholder="选择日期"
+          format="yyyy-MM-dd"
+          :options="options"
+          v-model="newContentForm.releaseTime"/>
+        </Form-item>
+        <Form-item label="内容名称" prop="name">
+          <Input v-model="newContentForm.name" placeholder="请输入内容名称" style="width: 480px"></Input>
+          <!-- <span v-if="item.type==3||item.newsType==4">9个字符以内</span>
+          <span v-else>6个字符以内</span> -->
+        </Form-item>
+        <Form-item label="链接地址" prop="url">
+          <Input v-model="newContentForm.url" placeholder="请输入链接地址" style="width: 480px"></Input>
+        </Form-item>
+        <Form-item label="首页展示" prop="isShow">
+          <RadioGroup v-model="newContentForm.isShow" type="button">
+            <Radio label="1">是</Radio>
+            <Radio label="0">否</Radio>
+          </RadioGroup>
+        </Form-item>
+        <Form-item label="顺序号" prop="showOrder">
+          <Input v-model="newContentForm.showOrder" placeholder="请输入顺序号" style="width: 200px"></Input>
+        </Form-item>
+       
+        <Form-item label="上传图片" prop="filePath">
+          <Upload
+            ref="upload"
+            type="drag"
+            :action="uploadUrl"
+            :headers="{'X-AIYANGNIU-SIGNATURE':token}"
+            :show-upload-list="false"
+            :on-success="handleSuccess"
+            :before-upload="beforeUpload"
+            :format="['jpg','jpeg','png']"
+            :max-size="2048"
+            :on-format-error="handleFormatError"
+            class="upload_outer_img">
+            <div v-if="!newContentForm.filePath" class="upload_border">
+              <p><Icon type="md-cloud-upload" size="35" color="#2db7f5" /></p>
+              <p>点击上传图片</p>
+            </div>
+            <div v-else> <img :src="newContentForm.filePath" width="100%" alt=""></div>
+          </Upload>
+          <div v-show="newContentForm.filePath" style="color:#2db7f5; cursor: pointer;" @click="gotoCheckImg(newContentForm.filePath)">
+            <i class="iconfont icon-htmal5icon09"></i> 查看大图
+          </div>
+          <div style="color:#aaa;font-size:12px;">图片格式为 jpg、jpeg、png</div>
+        </Form-item>
+        <Form-item label="简介" prop="intro">
+            <Input v-model="newContentForm.intro" placeholder="请输入内容简介" type="textarea" :autosize="{minRows: 2,maxRows: 5}" style="width: 480px"></Input>
+        </Form-item>
+        <Form-item label="内容详情" prop="content">
+          <BasicEditor ref="editotTool"></BasicEditor>
+        </Form-item>
+      </Form>
+    </div>
+    <div style="text-align:center;margin-bottom:3em;">
+      <Button @click="$router.back()" style="margin-right:5em;">取消返回</Button>
+      <Button type="primary" @click="conformSub">确定提交</Button>
+    </div>
+  </div>
+</template>
+
+<script>
+import { defineComponent,ref,reactive,onMounted,getCurrentInstance } from 'vue'
+import {compressFileM} from '@/utils/system/uploadCompress.js'
+import {content} from '@/request/api'
+import api from '@/request/apiConfig' //真实接口配置
+import {ElNotification} from 'element-plus'
+import BasicEditor from '@/components/wangEditor/BasicEditor.vue'
+import { ElMessage } from 'element-plus'
+import router from '@/router'
+export default defineComponent({
+  components:{
+    BasicEditor
+  },
+  setup() {
+    const token = localStorage.aynUserToken
+    let {proxy} = getCurrentInstance();
+    let newContentForm = reactive({contypeId: '',newsType: '',isShow: '1',showOrder: '',name:'',url:'',releaseTime:'',filePath: '',intro:'',content:''})
+    const options= {
+      disabledDate (date) {
+          return date && date.valueOf() < Date.now() - 86400000;
+      }
+    }
+    // 表单验证项
+    let newContentRule = ref({
+      contypeId: [{ required: true, message: '请选择所属类别', trigger: 'change' }],
+      newsType: [{ required: true, message: '请选择新闻类型', trigger: 'change' }],
+      releaseTime: [{ required: true, type:'date', message: '请选择发布时间', trigger: 'change' }],
+      name: [{ required: true, message: '请填写内容名称', trigger: 'blur' }],
+      isShow: [{ required: true, message: '请选择展示状态', trigger: 'change' }],
+    })
+    const uploadUrl = api.apiManager + '/manager/images/upload'
+
+    // 获取分类的列表
+    let typeDataList = ref([])
+    async function getTypeDataList(){
+      await content.GetContentTypeList({limit:10000,offset:0}).then((res) => {
+        if (res.code =='101') {
+          typeDataList.value = res.data || []
+        }
+      })
+    }
+    
+    function beforeUpload(file){
+      // 进行2.5倍系数压缩
+      return (compressFileM(file,2.5))
+    }
+
+    function handleFormatError (file) {
+      ElNotification.warning({
+        title: '温馨提示',
+        message: '文件格式不正确',
+        showClose: false,
+      })
+    }
+
+    function handleSuccess(res, file, fileList){
+      if(res.code == '101'){
+        newContentForm.filePath = res.data
+      }
+    }
+
+    // 打开新页面查看照片
+    function gotoCheckImg(pic){
+      window.open(pic,'blank')
+    }
+
+    // 编辑获取详情内容
+    async function getContentDetail(){
+      await content.GetContentDetail({id: router.currentRoute.value.query.id}).then(res => {
+        if(res.code == '101' && res.data) {
+          let _data = res.data
+          newContentForm = Object.assign(newContentForm, {
+            contypeId: _data.contypeId,
+            newsType: _data.newsType,
+            isShow: _data.isShow,
+            name: _data.name,
+            url: _data.url,
+            showOrder: _data.showOrder,
+            intro: _data.intro,
+            content: _data.content,
+            filePath: _data.filePath,
+            id:_data.id,
+            releaseTime: new Date(_data.releaseTime),
+          })
+          proxy.$refs.editotTool.valueHtml = _data.content
+        }
+      })
+    }
+
+    // 确定提交表单
+    function conformSub(){
+      newContentForm.content = proxy.$refs.editotTool.valueHtml  //富文本传值
+      let reqFn = null
+      if(newContentForm.id){
+        reqFn = content.UpdateContent
+      }else{
+        reqFn = content.AddNewContent
+      }
+      proxy.$refs['contentForm'].validate(async (valid) => {
+        if (valid) {
+          await reqFn(newContentForm).then(res=>{
+            if(res.code == 101){
+              ElMessage({
+                message: res.message || '添加成功!',
+                type: 'success',
+              })
+              router.back()
+            }else{
+              ElMessage({
+                message: res.message || '添加失败!',
+                type: 'warning',
+              })
+            }
+          }).catch(e=>{
+            ElMessage({
+              message: e.message || '添加失败!',
+              type: 'error',
+            })
+          })
+        } else {
+          ElMessage({
+            message:'请填写完表单数据!',
+            type: 'warning',
+          })
+        }
+      })
+    }
+
+    onMounted(async()=>{
+      getTypeDataList()
+      // 编辑获取详情
+      if(router.currentRoute.value.path === '/contentManage/editContent'){
+        getContentDetail()
+      }
+    })
+
+    return {
+      uploadUrl,token,options,
+      newContentForm,typeDataList,newContentRule,
+      handleFormatError,handleSuccess,beforeUpload,gotoCheckImg,conformSub
+    }
+  },
+
+})
+</script>
+
+<style lang="scss" scoped>
+.newContent{
+  padding: 1em;
+  .upload_outer_img{
+    max-width: 45%;
+    img{
+      width: 100%; padding: 0;
+    }
+    .upload_border{
+      width: 100%; padding: 3em;
+    }
+  }
+  
+}
+</style>
+<style lang="scss">
+.newContent{
+  .ivu-upload-drag{
+    border: 0px;
+  }
+}
+</style>

+ 95 - 0
src/views/dashboard/components/cardBox.vue

@@ -0,0 +1,95 @@
+<template>
+  <div class="cardBox" :style="'width:'+boxWidth">
+    <div class="card_border" :style="'background-image: linear-gradient(to right,'+startCor+','+endCor+');'">
+      <Row>
+        <Col span="16">
+          <p class="main_num">
+            <count-to :startVal='0' class='main_num' :endVal='numValue' :duration='2500'></count-to>
+            <span class="num_unit">{{numUnit}}</span>
+          </p>
+          <p class="sunTitle">{{mainTitle}}</p>
+        </Col>
+        <Col span="8">
+          <div class="other_conyent">
+            <p class="other_title">{{subTitle}}</p>
+          </div>
+        </Col>
+      </Row>
+    </div>
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { CountTo } from 'vue3-count-to';
+export default defineComponent({
+  components:{
+    CountTo
+  },
+  props:{
+    startCor:{
+      type:String,
+      default:'#23cde4'
+    },
+    endCor:{
+      type:String,
+      default:'#4b87f9'
+    },
+    boxWidth:{
+      type:String,
+      default:'20%'
+    },
+    numValue:{
+      type:Number,
+      default:0
+    },
+    mainTitle:{
+      type:String,
+      default:'暂无内容',
+    },
+    numUnit:{
+      type:String,
+      default:''
+    },
+    subTitle:{
+      type:String,
+      default:''
+    }
+  },
+  setup() {
+    return {
+      
+    }
+  },
+  methods: {
+    
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.cardBox{
+  .card_border{
+    height: 12vh;
+    border-radius: 8px; padding: 5%;
+    box-shadow: 0px 35px 30px -20px #ccc;
+    .main_num{
+      font-size:3.5vh; color: #fff; font-weight: bold;
+      .num_unit{
+        font-size:1.5vh; color: #fff; font-weight: 200; margin-left: 6px;
+      }
+    }
+    .sunTitle{
+      color: #fff; font-size: 1.2vh; margin-left: 2px;
+      margin-bottom: 10px;
+    }
+    .other_conyent{
+      text-align: right; margin-right: 5px;
+      .other_title{
+        color: #fff;
+        font-size: 1.5vh;
+      }
+    }
+  }
+}
+</style>

+ 87 - 0
src/views/dashboard/components/desCard.vue

@@ -0,0 +1,87 @@
+<template>
+  <div class="desCard" :style="'width:'+boxWidth">
+    <div class="topstep">
+      <Row justify="center" align="middle">
+        <Col span="8" >
+          <div class="flex-style">
+            <img src="@/assets/images/tasklogo.png" style="width:60%;" alt="">
+          </div>
+        </Col>
+        <Col span="16">
+          <div style="margin-left:10%;">
+            <p class="card_title">任务指标</p>
+            <p class="card_num">327 <span class="sec_title">(项)</span></p>
+          </div>
+        </Col>
+      </Row>
+    </div>
+
+    <div class="bottom_step">
+      <p>同比增长20 &nbsp;&nbsp;&nbsp; 环比增长10</p>
+    </div>
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+export default defineComponent({
+  props:{
+    startCor:{
+      type:String,
+      default:'#23cde4'
+    },
+    endCor:{
+      type:String,
+      default:'#4b87f9'
+    },
+    boxWidth:{
+      type:String,
+      default:'32%'
+    }
+  },
+  setup() {
+    return {
+      
+    }
+  },
+  methods: {
+    
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.desCard{
+  background-color: #fff; margin-bottom: 2%;
+  border: 1px solid #ddd;
+  height: 16vh;
+  .topstep{
+    height: 10vh;
+    .flex-style{
+      height: 10vh;
+      display: flex;
+      justify-content: center;
+      align-items:center;
+    }
+    .card_title{
+      color: #666;
+      font-weight: 600;
+    }
+    .card_num{
+      font-size:2.5vh;font-weight: 600;
+      color: #2db7f5;
+      .sec_title{
+        font-size:1vh; font-weight: 500;
+      }
+    }
+  }
+  .bottom_step{
+    text-align: center; height: 6vh; line-height: 6vh;
+    border-top: 2px solid #f1f1f1;
+    p{
+      font-size: 1.3vh;color: #0f9451;
+      font-weight: 500;
+    }
+  }
+}
+</style>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
src/views/dashboard/components/static/china.json


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
src/views/dashboard/components/static/shq.json


+ 89 - 0
src/views/dashboard/index.vue

@@ -0,0 +1,89 @@
+/**
+  // author:zhangb
+  // time:2022-10-19
+  // desc:可视化组件图标部分拆解
+*/
+<template>
+  <div class="dashboard">
+    <!-- 顶部卡片汇总 -->
+    <div class="top_card_border">
+      <card-box :boxWidth="'19%'" :numValue="8866" :mainTitle="'牧场总数量'" :numUnit="'个'"></card-box>
+      <card-box :startCor="'#f382ee'" :endCor="'#7d7cfe'" :boxWidth="'19%'" :numValue="65244" :mainTitle="'奶牛总数量'" :numUnit="'头'"></card-box>
+      <card-box :startCor="'#40ecb0'" :endCor="'#0eb4e8'" :boxWidth="'19%'" :numValue="10521" :mainTitle="'原奶总产量'" :numUnit="'公斤'"></card-box>
+      <card-box :startCor="'#fe8e82'" :endCor="'#ff6fb7'" :boxWidth="'19%'" :numValue="8452" :mainTitle="'草料需求量'" :numUnit="'万吨'" :subTitle="'(按每月)'"></card-box>
+      <card-box :startCor="'#fe8e82'" :endCor="'#ff6fb7'" :boxWidth="'19%'" :numValue="65121" :mainTitle="'饲料需求量'" :numUnit="'万吨'" :subTitle="'(按每月)'"></card-box>
+    </div>
+
+    <!-- TODO 暂不使用 -->
+    <!-- 中部区域 -->
+    <div class="mid_content" v-show="false">
+      <!-- 左边 -->
+      <div class="let_step">
+        <des-card></des-card>
+        <des-card></des-card>
+        <des-card></des-card>
+        <des-card></des-card>
+        <des-card></des-card>
+        <des-card></des-card>
+      </div>
+      <!-- echarts 图表 -->
+      <div class="right_step">
+        <pie-chart></pie-chart>
+      </div>
+    </div>
+    
+    <!-- 核心可视化地图内容 -->
+    <div class="map_style">
+      <map-chart :heightStyle="'height:800px;'"></map-chart>
+    </div>
+    
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import cardBox from './components/cardBox.vue'
+import desCard from './components/desCard.vue'
+export default defineComponent({
+  components:{
+    cardBox,desCard
+  },
+  setup() {
+    return {
+      
+    }
+  },
+  methods: {
+    
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.dashboard{
+  padding: 1em;
+  .top_card_border{
+    width: 100%;
+    display: flex;
+    justify-content:space-between;
+  }
+  .mid_content{
+    margin-top:30px;
+    display: flex; justify-content:space-between;
+    .let_step{
+      // border: 1px solid #000;
+      display: flex; justify-content:space-between;
+      width:55%; height: calc(35vh);
+      flex-wrap:wrap;
+    }
+    .right_step{
+      width: 44%; height: 34vh;
+      background-color: #fff;
+    }
+  }
+  .map_style{
+    border: 1px solid #000; margin-top:30px;
+    height: calc(100vh*0.7);
+  }
+}
+</style>

+ 0 - 0
src/views/intentManage/buyIntent.vue


برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است