Ver Fonte

feat: init

wangzs há 7 meses atrás
commit
d2f6b7ae52
100 ficheiros alterados com 7757 adições e 0 exclusões
  1. 3 0
      .env.development
  2. 3 0
      .env.localhost
  3. 3 0
      .env.pre
  4. 3 0
      .env.production
  5. 3 0
      .env.test
  6. 18 0
      .eslintignore
  7. 66 0
      .eslintrc.cjs
  8. 21 0
      .gitignore
  9. 30 0
      .prettierrc.cjs
  10. 20 0
      index.html
  11. 89 0
      package.json
  12. 10 0
      shims-uni.d.ts
  13. 22 0
      src/App.vue
  14. 31 0
      src/api/business/goods/goods-api.js
  15. 37 0
      src/api/business/oa/enterprise-api.js
  16. 33 0
      src/api/business/oa/notice-api.js
  17. 24 0
      src/api/support/change-log-api.js
  18. 59 0
      src/api/support/dict-api.js
  19. 17 0
      src/api/support/feedback-api.js
  20. 14 0
      src/api/support/file-api.js
  21. 16 0
      src/api/support/message-api.js
  22. 53 0
      src/api/system/login-api.js
  23. 55 0
      src/components/dict-select/index.vue
  24. 70 0
      src/components/smart-card/index.vue
  25. 73 0
      src/components/smart-detail-tabs/index.vue
  26. 42 0
      src/components/smart-enum-radio/index.vue
  27. 49 0
      src/components/smart-enum-select/index.vue
  28. 26 0
      src/constants/business/erp/goods-const.js
  29. 24 0
      src/constants/business/oa/enterprise-const.js
  30. 70 0
      src/constants/common-const.js
  31. 28 0
      src/constants/index.js
  32. 19 0
      src/constants/local-storage-key-const.js
  33. 28 0
      src/constants/regular-const.js
  34. 32 0
      src/constants/support/change-log-const.js
  35. 31 0
      src/constants/support/file-const.js
  36. 30 0
      src/constants/support/message-const.js
  37. 31 0
      src/constants/system/login-device-const.js
  38. 120 0
      src/lib/encrypt.js
  39. 128 0
      src/lib/smart-request.js
  40. 22 0
      src/lib/smart-sentry.js
  41. 30 0
      src/lib/smart-support.js
  42. 22 0
      src/main.js
  43. 72 0
      src/manifest.json
  44. 212 0
      src/pages.json
  45. 245 0
      src/pages/enterprise/enterprise-detail.vue
  46. 184 0
      src/pages/enterprise/enterprise-form.vue
  47. 217 0
      src/pages/enterprise/enterprise-list.vue
  48. 94 0
      src/pages/form/form.vue
  49. 68 0
      src/pages/goods/components/goods-list.vue
  50. 100 0
      src/pages/goods/components/goods-query-form-popup.vue
  51. 222 0
      src/pages/goods/goods-index.vue
  52. 44 0
      src/pages/home/components/banner.vue
  53. 135 0
      src/pages/home/components/goods.vue
  54. 131 0
      src/pages/home/components/menu.vue
  55. 96 0
      src/pages/home/components/notice.vue
  56. 87 0
      src/pages/home/components/statistics.vue
  57. 80 0
      src/pages/home/index.vue
  58. 154 0
      src/pages/list/components/list-ui1.vue
  59. 143 0
      src/pages/list/components/list-ui2.vue
  60. 335 0
      src/pages/list/components/list-ui3.vue
  61. 246 0
      src/pages/list/components/list-ui4.vue
  62. 47 0
      src/pages/list/list.vue
  63. 108 0
      src/pages/list2/components/course-list.vue
  64. 149 0
      src/pages/list2/components/discount-list.vue
  65. 159 0
      src/pages/list2/components/express-list.vue
  66. 94 0
      src/pages/list2/components/iot-list.vue
  67. 100 0
      src/pages/list2/components/service-list.vue
  68. 55 0
      src/pages/list2/list.vue
  69. 57 0
      src/pages/login/components/login-check-box.vue
  70. 141 0
      src/pages/login/components/other-way-box.vue
  71. 366 0
      src/pages/login/login.vue
  72. 166 0
      src/pages/message/message.vue
  73. 112 0
      src/pages/mine/components/mine-menu.vue
  74. 111 0
      src/pages/mine/components/mine-user-blue.vue
  75. 175 0
      src/pages/mine/components/mine-user-white.vue
  76. 66 0
      src/pages/mine/mine.vue
  77. 72 0
      src/pages/notice/components/notice-list.vue
  78. 115 0
      src/pages/notice/components/notice-query-form-popup.vue
  79. 76 0
      src/pages/notice/notice-detail.vue
  80. 224 0
      src/pages/notice/notice-index.vue
  81. 118 0
      src/pages/order-detail/components/detail-model-path.vue
  82. 32 0
      src/pages/order-detail/components/order-detail-base-info.vue
  83. 102 0
      src/pages/order-detail/components/order-detail-settle.vue
  84. 52 0
      src/pages/order-detail/order-detail.vue
  85. 143 0
      src/pages/select-people/select-people.vue
  86. 79 0
      src/pages/support/change-log/change-log-detail.vue
  87. 197 0
      src/pages/support/change-log/change-log-list.vue
  88. 168 0
      src/pages/support/feedback/feedback-form.vue
  89. 97 0
      src/plugins/smart-enums-plugin.js
  90. 6 0
      src/shime-uni.d.ts
  91. BIN
      src/static/common/back-icon.png
  92. BIN
      src/static/common/update-app.png
  93. BIN
      src/static/images/form/add-image.png
  94. BIN
      src/static/images/form/add.png
  95. BIN
      src/static/images/form/back.png
  96. BIN
      src/static/images/form/close-image.png
  97. BIN
      src/static/images/form/submit.png
  98. BIN
      src/static/images/form/title-bg.png
  99. BIN
      src/static/images/home/admin-icon.png
  100. BIN
      src/static/images/home/copy-icon.png

+ 3 - 0
.env.development

@@ -0,0 +1,3 @@
+NODE_ENV=development
+VITE_APP_TITLE='SmartAdmin 开发环境(Dev)'
+VITE_APP_API_URL='http://127.0.0.1:1024'

+ 3 - 0
.env.localhost

@@ -0,0 +1,3 @@
+NODE_ENV=development
+VITE_APP_TITLE='SmartH5 本地环境(Local)'
+VITE_APP_API_URL='http://127.0.0.1:1024'

+ 3 - 0
.env.pre

@@ -0,0 +1,3 @@
+NODE_ENV=production
+VITE_APP_TITLE='SmartH5 预发布环境(Pre)'
+VITE_APP_API_URL='https://app.smartadmin.vip/smart-app-api'

+ 3 - 0
.env.production

@@ -0,0 +1,3 @@
+NODE_ENV=production
+VITE_APP_TITLE='Smart App V3.X'
+VITE_APP_API_URL='https://app.smartadmin.vip/smart-app-api'

+ 3 - 0
.env.test

@@ -0,0 +1,3 @@
+NODE_ENV=production
+VITE_APP_TITLE='SmartH5 测试环境(Test)'
+VITE_APP_API_URL='http://127.0.0.1:1024'

+ 18 - 0
.eslintignore

@@ -0,0 +1,18 @@
+
+*.sh
+node_modules
+lib
+*.md
+*.woff
+*.ttf
+.vscode
+.idea
+dist
+public
+/docs
+.husky
+.local
+.localhost
+/bin
+Dockerfile
+src/assets

+ 66 - 0
.eslintrc.cjs

@@ -0,0 +1,66 @@
+/*
+ * @Description:
+ * @Author: zhuoda
+ * @Date: 2021-11-05
+ * @LastEditTime: 2022-07-05
+ * @LastEditors: zhuoda
+ */
+module.exports = {
+  root: true, //此项是用来告诉eslint找当前配置文件不能往父级查找
+  env: {
+    browser: true,
+    es2021: true,
+    node: true,
+  },
+  parser: 'vue-eslint-parser', //使用vue-eslint-parser 来解析vue文件中的 template和script
+  parserOptions: {
+    ecmaVersion: 12, // 默认情况下,ESLint使用的是ECMAScript5语法,此处我们设置的选项是 es12
+    sourceType: 'module', // 指定js导入的方式
+  },
+  extends: ['plugin:vue/vue3-essential', 'eslint:recommended', 'plugin:vue/base'],
+  globals: {
+    defineProps: 'readonly',
+    defineEmits: 'readonly',
+    defineExpose: 'readonly',
+    withDefaults: 'readonly',
+  },
+  plugins: ['vue'],
+  rules: {
+    'no-unused-vars': [
+      'error',
+      // we are only using this rule to check for unused arguments since TS
+      // catches unused variables but not args.
+      { varsIgnorePattern: '.*', args: 'none' },
+    ],
+    'space-before-function-paren': 'off',
+
+    'vue/attributes-order': 'off',
+    'vue/one-component-per-file': 'off',
+    'vue/html-closing-bracket-newline': 'off',
+    'vue/max-attributes-per-line': 'off',
+    'vue/multiline-html-element-content-newline': 'off',
+    'vue/singleline-html-element-content-newline': 'off',
+    'vue/attribute-hyphenation': 'off',
+    'vue/require-default-prop': 'off',
+    'vue/multi-word-component-names': [
+      'error',
+      {
+        ignores: ['index'], //需要忽略的组件名
+      },
+    ],
+    'vue/html-self-closing': [
+      'error',
+      {
+        html: {
+          void: 'always',
+          normal: 'never',
+          component: 'always',
+        },
+        svg: 'always',
+        math: 'always',
+      },
+    ],
+    // Enable vue/script-setup-uses-vars rule
+    'vue/script-setup-uses-vars': 'error',
+  },
+};

+ 21 - 0
.gitignore

@@ -0,0 +1,21 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+*.local
+
+# Editor directories and files
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 30 - 0
.prettierrc.cjs

@@ -0,0 +1,30 @@
+/*
+ * 代码格式化配置
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-12 14:44:18
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+module.exports = {
+  printWidth: 150, // 每行代码长度(默认80)
+  tabWidth: 2, // 缩进空格数
+  useTabs: false, //不用tab缩进
+  semi: true, //// 在语句末尾打印分号
+  singleQuote: true, // 使用单引号而不是双引号
+  vueIndentScriptAndStyle: true, //Vue文件脚本和样式标签缩进
+  quoteProps: 'as-needed', // 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
+  jsxSingleQuote: true, // 在JSX中使用单引号而不是双引号
+  trailingComma: 'es5', //多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
+  bracketSpacing: true, // 在对象文字中的括号之间打印空格
+  jsxBracketSameLine: false, //jsx 标签的反尖括号需要换行
+  arrowParens: 'always', // 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
+  rangeStart: 0, // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
+  rangeEnd: Infinity,
+  requirePragma: false, // 指定要使用的解析器,不需要写文件开头的 @prettier
+  insertPragma: false, // 不需要自动在文件开头插入 @prettier
+  proseWrap: 'preserve', // 使用默认的折行标准 always\never\preserve
+  htmlWhitespaceSensitivity: 'css', // 指定HTML文件的全局空格敏感度 css\strict\ignore
+  endOfLine: 'auto', // 因为prettier的规范和eslint的换行规则不同,所以这个必须配置。要不然每次打开文件都会有一堆的警告;换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr
+};

+ 20 - 0
index.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 89 - 0
package.json

@@ -0,0 +1,89 @@
+{
+  "name": "smart-app",
+  "version": "3.0.0",
+  "author": {
+    "name": "1024创新实验室(1024lab)",
+    "email": "lab1024@163.com",
+    "url": "https://www.1024lab.net"
+  },
+  "license": "MIT",
+  "homepage": "https://smartadmin.1024lab.net",
+  "scripts": {
+    "dev:app": "uni -p app",
+    "dev:app-android": "uni -p app-android",
+    "dev:app-ios": "uni -p app-ios",
+    "dev:custom": "uni -p",
+    "dev:h5": "uni",
+    "dev:h5:ssr": "uni --ssr",
+    "dev:mp-alipay": "uni -p mp-alipay",
+    "dev:mp-baidu": "uni -p mp-baidu",
+    "dev:mp-jd": "uni -p mp-jd",
+    "dev:mp-kuaishou": "uni -p mp-kuaishou",
+    "dev:mp-lark": "uni -p mp-lark",
+    "dev:mp-qq": "uni -p mp-qq",
+    "dev:mp-toutiao": "uni -p mp-toutiao",
+    "dev:mp-weixin": "uni -p mp-weixin",
+    "dev:mp-xhs": "uni -p mp-xhs",
+    "dev:quickapp-webview": "uni -p quickapp-webview",
+    "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
+    "dev:quickapp-webview-union": "uni -p quickapp-webview-union",
+    "build:app": "uni build -p app",
+    "build:app-android": "uni build -p app-android",
+    "build:app-ios": "uni build -p app-ios",
+    "build:custom": "uni build -p",
+    "build:h5": "uni build",
+    "build:h5:ssr": "uni build --ssr",
+    "build:mp-alipay": "uni build -p mp-alipay",
+    "build:mp-baidu": "uni build -p mp-baidu",
+    "build:mp-jd": "uni build -p mp-jd",
+    "build:mp-kuaishou": "uni build -p mp-kuaishou",
+    "build:mp-lark": "uni build -p mp-lark",
+    "build:mp-qq": "uni build -p mp-qq",
+    "build:mp-toutiao": "uni build -p mp-toutiao",
+    "build:mp-weixin": "uni build -p mp-weixin",
+    "build:mp-xhs": "uni build -p mp-xhs",
+    "build:quickapp-webview": "uni build -p quickapp-webview",
+    "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
+    "build:quickapp-webview-union": "uni build -p quickapp-webview-union"
+  },
+  "dependencies": {
+    "@dcloudio/uni-app": "3.0.0-3090920231225001",
+    "@dcloudio/uni-app-plus": "3.0.0-3090920231225001",
+    "@dcloudio/uni-components": "3.0.0-3090920231225001",
+    "@dcloudio/uni-h5": "3.0.0-3090920231225001",
+    "@dcloudio/uni-mp-alipay": "3.0.0-3090920231225001",
+    "@dcloudio/uni-mp-baidu": "3.0.0-3090920231225001",
+    "@dcloudio/uni-mp-jd": "3.0.0-3090920231225001",
+    "@dcloudio/uni-mp-kuaishou": "3.0.0-3090920231225001",
+    "@dcloudio/uni-mp-lark": "3.0.0-3090920231225001",
+    "@dcloudio/uni-mp-qq": "3.0.0-3090920231225001",
+    "@dcloudio/uni-mp-toutiao": "3.0.0-3090920231225001",
+    "@dcloudio/uni-mp-weixin": "3.0.0-3090920231225001",
+    "@dcloudio/uni-mp-xhs": "3.0.0-3090920231225001",
+    "@dcloudio/uni-quickapp-webview": "3.0.0-3090920231225001",
+    "@dcloudio/uni-ui": "1.5.0",
+    "crypto-js": "4.1.1",
+    "dayjs": "1.11.10",
+    "lodash": "4.17.21",
+    "pinia": "2.0.36",
+    "sm-crypto": "0.3.13",
+    "vue": "3.2.47",
+    "vue-i18n": "9.1.9"
+  },
+  "devDependencies": {
+    "@dcloudio/types": "3.3.2",
+    "@dcloudio/uni-automator": "3.0.0-3090920231225001",
+    "@dcloudio/uni-cli-shared": "3.0.0-3090920231225001",
+    "@dcloudio/uni-stacktracey": "3.0.0-3090920231225001",
+    "@dcloudio/vite-plugin-uni": "3.0.0-3090920231225001",
+    "@vue/runtime-core": "3.2.45",
+    "eslint": "8.16.0",
+    "eslint-config-prettier": "9.0.0",
+    "eslint-plugin-prettier": "5.0.0",
+    "eslint-plugin-vue": "9.17.0",
+    "prettier": "3.0.2",
+    "sass": "1.69.7",
+    "sass-loader": "10.1.1",
+    "vite": "4.0.3"
+  }
+}

+ 10 - 0
shims-uni.d.ts

@@ -0,0 +1,10 @@
+/// <reference types='@dcloudio/types' />
+import 'vue'
+
+declare module '@vue/runtime-core' {
+  type Hooks = App.AppInstance & Page.PageInstance;
+
+  interface ComponentCustomOptions extends Hooks {
+
+  }
+}

+ 22 - 0
src/App.vue

@@ -0,0 +1,22 @@
+<script>
+  import { useUserStore } from '@/store/modules/system/user';
+  export default {
+    onLaunch: function () {
+      useUserStore().getLoginInfo();
+    },
+    onShow: function () {
+      console.log('App Show');
+    },
+    onHide: function () {
+      console.log('App Hide');
+    },
+  };
+</script>
+
+<style lang="scss">
+  @import '@/uni_modules/uni-scss/index.scss';
+  /* 设置基准字体大小为16px */
+  body {
+    font-size: 16px;
+  }
+</style>

+ 31 - 0
src/api/business/goods/goods-api.js

@@ -0,0 +1,31 @@
+/*
+ * @Description:
+ * @Author: zhuoda
+ * @Date: 2021-11-05
+ * @LastEditTime: 2022-06-23
+ * @LastEditors: zhuoda
+ */
+import { postRequest, getRequest } from '@/lib/smart-request';
+
+export const goodsApi = {
+  // 添加商品 @author zhuoda
+  addGoods: (param) => {
+    return postRequest('/goods/add', param);
+  },
+  // 删除 @author zhuoda
+  deleteGoods: (goodsId) => {
+    return getRequest(`/goods/delete/${goodsId}`);
+  },
+  // 批量 @author zhuoda
+  batchDelete: (goodsIdList) => {
+    return postRequest('/goods/batchDelete', goodsIdList);
+  },
+  // 分页查询 @author zhuoda
+  queryGoodsList: (param) => {
+    return postRequest('/goods/query', param);
+  },
+  // 更新商品 @author zhuoda
+  updateGoods: (param) => {
+    return postRequest('/goods/update', param);
+  },
+};

+ 37 - 0
src/api/business/oa/enterprise-api.js

@@ -0,0 +1,37 @@
+/*
+ * 企业信息
+ *
+ * @Author:    开云
+ * @Date:      2023-09-03 21:47:28
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+import { postRequest, getRequest } from '@/lib/smart-request';
+
+export const enterpriseApi = {
+  // 新建企业 @author 开云
+  create: (param) => {
+    return postRequest('/oa/enterprise/create', param);
+  },
+
+  // 查询企业详情 @author 开云
+  detail: (enterpriseId) => {
+    return getRequest(`/oa/enterprise/get/${enterpriseId}`);
+  },
+
+  // 分页查询企业模块 @author 开云
+  pageQuery: (param) => {
+    return postRequest('/oa/enterprise/page/query', param);
+  },
+
+  // 编辑企业 @author 开云
+  update: (param) => {
+    return postRequest('/oa/enterprise/update', param);
+  },
+
+  // 删除企业 @author 开云
+  delete: (enterpriseId) => {
+    return getRequest(`/oa/enterprise/delete/${enterpriseId}`);
+  },
+};

+ 33 - 0
src/api/business/oa/notice-api.js

@@ -0,0 +1,33 @@
+/*
+ * @Description: 公告信息、企业动态
+ * @version:
+ * @Author: zhuoda
+ * @Date: 2022-08-16 20:34:36
+ */
+import { postRequest, getRequest } from '@/lib/smart-request';
+
+export const noticeApi = {
+  // ---------------- 通知公告类型 -----------------------
+
+  // 通知公告类型-获取全部 @author zhuoda
+  getAllNoticeTypeList() {
+    return getRequest('/oa/noticeType/getAll');
+  },
+
+  // --------------------- 【员工】查看 通知公告 -------------------------
+
+  // 通知公告-员工-查看详情 @author zhuoda
+  view(noticeId) {
+    return getRequest(`/oa/notice/employee/view/${noticeId}`);
+  },
+
+  // 通知公告-员工-查询 @author zhuoda
+  queryEmployeeNotice(param) {
+    return postRequest('/oa/notice/employee/query', param);
+  },
+
+  // 【员工】通知公告-查询 查看记录 @author zhuoda
+  queryViewRecord(param) {
+    return postRequest('/oa/notice/employee/queryViewRecord', param);
+  },
+};

+ 24 - 0
src/api/support/change-log-api.js

@@ -0,0 +1,24 @@
+/**
+ * 系统更新日志 api 封装
+ *
+ * @Author:    卓大
+ * @Date:      2022-09-26 14:53:50
+ * @Copyright  1024创新实验室
+ */
+import { postRequest, getRequest } from '@/lib/smart-request';
+
+export const changeLogApi = {
+  /**
+   * 分页查询  @author  卓大
+   */
+  queryPage: (param) => {
+    return postRequest('/support/changeLog/queryPage', param);
+  },
+
+  /**
+   * 详情  @author  卓大
+   */
+  getDetail: (changeLogId) => {
+    return getRequest(`/support/changeLog/getDetail/${changeLogId}`);
+  },
+};

+ 59 - 0
src/api/support/dict-api.js

@@ -0,0 +1,59 @@
+/*
+ * 字典
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-03 21:55:25
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+import { postRequest, getRequest } from '@/lib/smart-request';
+
+export const dictApi = {
+  // 分页查询数据字典KEY - @author 卓大
+  keyQuery: (param) => {
+    return postRequest('/support/dict/key/query', param);
+  },
+  // 查询全部字典key - @author 卓大
+  queryAllKey: () => {
+    return getRequest('/support/dict/key/queryAll');
+  },
+  /**
+   * 分页查询数据字典value - @author 卓大
+   */
+  valueQuery: (param) => {
+    return postRequest('/support/dict/value/query', param);
+  },
+  // 数据字典KEY-添加- @author 卓大
+  keyAdd: (param) => {
+    return postRequest('/support/dict/key/add', param);
+  },
+  // 分页查询数据字典value - @author 卓大
+  valueAdd: (param) => {
+    return postRequest('/support/dict/value/add', param);
+  },
+  // 数据字典key-更新- @author 卓大
+  keyEdit: (param) => {
+    return postRequest('/support/dict/key/edit', param);
+  },
+  // 数据字典Value-更新- @author 卓大
+  valueEdit: (param) => {
+    return postRequest('/support/dict/value/edit', param);
+  },
+  // 数据字典key-删除- @author 卓大
+  keyDelete: (keyIdList) => {
+    return postRequest('/support/dict/key/delete', keyIdList);
+  },
+  // 数据字典Value-删除- @author 卓大
+  valueDelete: (valueIdList) => {
+    return postRequest('/support/dict/value/delete', valueIdList);
+  },
+  // 缓存刷新- @author 卓大
+  cacheRefresh: () => {
+    return getRequest('/support/dict/cache/refresh');
+  },
+  // 数据字典-值列表- @author 卓大
+  valueList: (keyCode) => {
+    return getRequest(`/support/dict/value/list/${keyCode}`);
+  },
+};

+ 17 - 0
src/api/support/feedback-api.js

@@ -0,0 +1,17 @@
+/*
+ * 意见反馈
+ *
+ * @Author:    1024创新实验室:开云
+ * @Date:      2022-09-03 21:56:31
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+import { postRequest } from '/src/lib/smart-request';
+
+export const feedbackApi = {
+  // 意见反馈-新增
+  addFeedback: (params) => {
+    return postRequest('/support/feedback/add', params);
+  },
+};

+ 14 - 0
src/api/support/file-api.js

@@ -0,0 +1,14 @@
+/**
+ * 系统更新日志 api 封装
+ *
+ * @Author:    卓大
+ * @Date:      2022-09-26 14:53:50
+ * @Copyright  1024创新实验室
+ */
+import { uploadRequest } from '@/lib/smart-request';
+
+export const fileApi = {
+  upload: (file, folder) => {
+    return uploadRequest(file, folder);
+  },
+};

+ 16 - 0
src/api/support/message-api.js

@@ -0,0 +1,16 @@
+import { postRequest, getRequest } from '@/lib/smart-request';
+
+export const messageApi = {
+  // 通知消息-分页查询
+  queryMessage: (param) => {
+    return postRequest('/support/message/queryMyMessage', param);
+  },
+  // 通知消息-查询未读消息数
+  queryUnreadCount: () => {
+    return getRequest('/support/message/getUnreadCount');
+  },
+  // 通知消息-标记已读
+  updateReadFlag: (messageId) => {
+    return getRequest(`/support/message/read/${messageId}`);
+  },
+};

+ 53 - 0
src/api/system/login-api.js

@@ -0,0 +1,53 @@
+/*
+ *  登录
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-03 21:59:58
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+import { getRequest, postRequest } from '@/lib/smart-request';
+
+export const loginApi = {
+  /**
+   * 登录 @author 卓大
+   */
+  login: (param) => {
+    return postRequest('/login', param);
+  },
+
+  /**
+   * 退出登录 @author 卓大
+   */
+  logout: () => {
+    return getRequest('/login/logout');
+  },
+
+  /**
+   * 获取验证码 @author 卓大
+   */
+  getCaptcha: () => {
+    return getRequest('/login/getCaptcha');
+  },
+
+  /**
+   * 获取登录信息 @author 卓大
+   */
+  getLoginInfo: () => {
+    return getRequest('/login/getLoginInfo');
+  },
+   /**
+   * 获取双因子登录标识 @author 卓大
+   */
+   getTwoFactorLoginFlag: () => {
+    return getRequest('/login/getTwoFactorLoginFlag');
+  },
+    /**
+   * 获取邮箱登录验证码 @author 卓大
+   */
+    sendLoginEmailCode: (loginName) => {
+      return getRequest(`/login/sendEmailCode/${loginName}`);
+    },
+  
+};

+ 55 - 0
src/components/dict-select/index.vue

@@ -0,0 +1,55 @@
+<!---
+  * 字段 下拉选择框
+  * 
+  * @Author:    1024创新实验室:罗伊
+  * @Date:      2022-09-12 22:06:45 
+  * @Wechat:    zhuda1024 
+  * @Email:     lab1024@163.com 
+  * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012 
+  *
+-->
+<template>
+  <div>
+    <uni-data-select :localdata="dictValueList" v-model="selectValue" :placeholder="props.placeholder" :clear="true" @change="onChange" />
+  </div>
+</template>
+
+<script setup>
+  import { onMounted, ref, watch } from 'vue';
+  import { dictApi } from '@/api/support/dict-api';
+
+  const props = defineProps({
+    keyCode: String,
+    modelValue: [String],
+    placeholder: {
+      type: String,
+      default: '请选择',
+    },
+  });
+
+  // -------------------------- 查询 字典数据 --------------------------
+
+  const dictValueList = ref([]);
+  async function queryDict() {
+    let res = await dictApi.valueList(props.keyCode);
+    dictValueList.value = res.data.map((e) => Object.assign({}, { text: e.valueName, value: e.valueCode }));
+  }
+  onMounted(queryDict);
+
+  // -------------------------- 选中 相关、事件 --------------------------
+
+  const selectValue = ref(props.modelValue);
+  watch(
+    () => props.modelValue,
+    (value) => {
+      selectValue.value = value;
+    }
+  );
+
+  const emit = defineEmits(['update:modelValue', 'change']);
+  function onChange(selectValue) {
+    let find = dictValueList.value.filter((e) => e.value === selectValue)[0];
+    emit('update:modelValue', find.value);
+    emit('change', find.value, find.text);
+  }
+</script>

+ 70 - 0
src/components/smart-card/index.vue

@@ -0,0 +1,70 @@
+<template>
+	<view class="card">
+		<view class="card-header">
+			<slot name="title">
+				<view class="card-title">
+					{{ title }}
+				</view>
+			</slot>
+			<slot v-if="isExtra" name="extra" @click="extra">
+				<view class="card-extra">
+					查看更多>
+				</view>
+			</slot>
+		</view>
+		<view class="card-content">
+			<slot name="content"></slot>
+		</view>
+	</view>
+</template>
+
+<script setup>
+	const props = defineProps({
+		//标题
+		title:{
+			type:String,
+			default:''
+		},
+		//是否展示Extra
+		isExtra:{
+			type:Boolean,
+			default:true
+		}
+	})
+	const emits = defineEmits(['extra'])
+	const extra = ()=>{
+		emits('extra')
+	}
+</script>
+
+<style lang="scss">
+.card{
+	width: 700rpx;
+	margin: 0 auto 20rpx;
+	border-radius: 12rpx;
+	overflow: hidden;
+	.card-header{
+		height: 76rpx;
+		background: linear-gradient(180deg,#e8f4ff, #f8fcff);
+		display: flex;
+		justify-content: space-between;
+		padding: 0 30rpx;
+		align-items: center;
+		.card-title{
+			font-size: 32rpx;
+			font-family: PingFang SC, PingFang SC-Semibold;
+			font-weight: 600;
+			text-align: left;
+			color: #323333;
+		}
+		.card-extra{
+			cursor: pointer;
+			font-size: 24rpx;
+			font-family: PingFang SC, PingFang SC-Regular;
+			font-weight: 400;
+			text-align: center;
+			color: #1a9aff;
+		}
+	}
+}
+</style>

+ 73 - 0
src/components/smart-detail-tabs/index.vue

@@ -0,0 +1,73 @@
+<template>
+  <scroll-view class="scroll-view" scroll-x="true" :show-scrollbar="false">
+    <view class="item" :class="active === item.value ? 'active' : ''" v-for="item in tabsList" :key="item.value" @click="change(item.value)">
+      {{ item.label }}
+    </view>
+  </scroll-view>
+</template>
+
+<script setup>
+  import { ref, watch } from 'vue';
+  const props = defineProps({
+    modelValue: {
+      type: Number,
+      default: 0,
+    },
+    position: {
+      type: String,
+      default: 'fixed',
+    },
+    tabsList: {
+      type: Array,
+      default: [],
+    },
+  });
+
+  const position = ref(props.position);
+  const active = ref(props.modelValue);
+
+  watch(
+    () => props.modelValue,
+    (newValue) => {
+      active.value = newValue;
+    }
+  );
+
+  const emit = defineEmits(['update:modelValue', 'change']);
+
+  const change = (value) => {
+    active.value = value;
+    emit('update:modelValue', value);
+    emit('change', value);
+  };
+</script>
+
+<style lang="scss" scoped>
+  .scroll-view {
+    position: v-bind(position);
+    background-color: #f4f4f4;
+    z-index: 998;
+
+    :deep(::-webkit-scrollbar) {
+      height: 0 !important;
+      width: 0 !important;
+      background: transparent;
+    }
+
+    .item {
+      padding: 0 24rpx;
+      height: 72rpx;
+      font-size: 30rpx;
+      color: #777;
+      background: #fff;
+      display: inline-block;
+      border-radius: 8rpx;
+      line-height: 72rpx;
+      margin: 24rpx 0 24rpx 24rpx;
+    }
+    .active {
+      color: #ffffff;
+      background: #1a9aff;
+    }
+  }
+</style>

+ 42 - 0
src/components/smart-enum-radio/index.vue

@@ -0,0 +1,42 @@
+<!--
+  * 枚举 radio
+  * 
+  * @Author:    1024创新实验室-主任:卓大 
+  * @Date:      2022-08-08 20:32:30 
+  * @Wechat:    zhuda1024 
+  * @Email:     lab1024@163.com 
+  * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012 
+  *
+-->
+<template>
+  <radio-group @change="handleChange">
+    <label v-for="item in $smartEnumPlugin.getValueDescList(props.enumName)" :key="item.value" class="smart-margin-right10">
+      <radio :value="item.value + ''" :checked="item.value === modelValue">{{ item.desc }}</radio>
+    </label>
+  </radio-group>
+</template>
+
+<script setup>
+  import { ref, watch } from 'vue';
+
+  const props = defineProps({
+    enumName: String,
+    modelValue: [Number, String],
+  });
+
+  const emit = defineEmits(['update:modelValue', 'change']);
+
+  const selectValue = ref(props.modelValue);
+
+  watch(
+    () => props.modelValue,
+    (newValue) => {
+      selectValue.value = newValue;
+    }
+  );
+
+  function handleChange(e) {
+    emit('update:modelValue', e.detail.value);
+    emit('change', e.detail.value);
+  }
+</script>

+ 49 - 0
src/components/smart-enum-select/index.vue

@@ -0,0 +1,49 @@
+<!---
+  * 字段 下拉选择框
+  * 
+  * @Author:    1024创新实验室:罗伊
+  * @Date:      2022-09-12 22:06:45 
+  * @Wechat:    zhuda1024 
+  * @Email:     lab1024@163.com 
+  * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012 
+  *
+-->
+<template>
+  <div>
+    <uni-data-select :localdata="dataList" v-model="selectValue" :clear="true" @change="onChange" />
+  </div>
+</template>
+
+<script setup>
+  import { inject, onMounted, ref, watch } from 'vue';
+
+  const smartEnumPlugin = inject('smartEnumPlugin');
+
+  const props = defineProps({
+    enumName: String,
+    modelValue: [Number, String],
+  });
+
+  // -------------------------- 枚举数据列表 --------------------------
+  const dataList = ref([]);
+  function getEnumData() {
+    dataList.value = smartEnumPlugin.getValueDescList(props.enumName).map((e) => Object.assign({}, { text: e.desc, value: e.value }));
+  }
+  onMounted(getEnumData);
+
+  // -------------------------- 选中 相关、事件 --------------------------
+
+  const selectValue = ref(props.modelValue);
+  watch(
+    () => props.modelValue,
+    (value) => {
+      selectValue.value = value;
+    }
+  );
+
+  const emit = defineEmits(['update:modelValue', 'change']);
+  function onChange(value) {
+    emit('update:modelValue', value);
+    emit('change', value, smartEnumPlugin.getDescByValue(props.enumName, value));
+  }
+</script>

+ 26 - 0
src/constants/business/erp/goods-const.js

@@ -0,0 +1,26 @@
+/*
+ * 商品
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-03 22:08:10
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+export const GOODS_STATUS_ENUM = {
+  APPOINTMENT: {
+    value: 1,
+    desc: '预约中',
+  },
+  SELL: {
+    value: 2,
+    desc: '售卖中',
+  },
+  SELL_OUT: {
+    value: 3,
+    desc: '售罄',
+  },
+};
+export default {
+  GOODS_STATUS_ENUM,
+};

+ 24 - 0
src/constants/business/oa/enterprise-const.js

@@ -0,0 +1,24 @@
+/*
+ * 企业
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2023-09-03 22:07:27
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+
+export const ENTERPRISE_TYPE_ENUM = {
+  NORMAL: {
+    value: 1,
+    desc: '有限企业',
+  },
+  FOREIGN: {
+    value: 2,
+    desc: '外资企业',
+  },
+};
+
+export default {
+  ENTERPRISE_TYPE_ENUM,
+};

+ 70 - 0
src/constants/common-const.js

@@ -0,0 +1,70 @@
+/*
+ * 通用常量
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-06 19:57:29
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+
+export const PAGE_SIZE = 10;
+
+export const PAGE_SIZE_OPTIONS = ['5', '10', '15', '20', '30', '40', '50', '75', '100', '150', '200', '300', '500'];
+
+//登录页面名字
+export const PAGE_PATH_LOGIN = '/login';
+
+//404页面名字
+export const PAGE_PATH_404 = '/404';
+
+export const showTableTotal = function (total) {
+  return `共${total}条`;
+};
+
+export const FLAG_NUMBER_ENUM = {
+  TRUE: {
+    value: 1,
+    desc: '是',
+  },
+  FALSE: {
+    value: 0,
+    desc: '否',
+  },
+};
+
+export const GENDER_ENUM = {
+  UNKNOWN: {
+    value: 0,
+    desc: '未知',
+  },
+  MAN: {
+    value: 1,
+    desc: '男',
+  },
+  WOMAN: {
+    value: 2,
+    desc: '女',
+  },
+};
+
+export const USER_TYPE_ENUM = {
+  ADMIN_EMPLOYEE: {
+    value: 1,
+    desc: '员工',
+  },
+  
+};
+
+export const DATA_TYPE_ENUM = {
+  NORMAL: {
+    value: 1,
+    desc: '普通',
+  },
+  ENCRYPT: {
+    value: 10,
+    desc: '加密',
+  },
+  
+};
+

+ 28 - 0
src/constants/index.js

@@ -0,0 +1,28 @@
+/*
+ * 所有常量入口
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-06 19:58:28
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+import { FLAG_NUMBER_ENUM, GENDER_ENUM, USER_TYPE_ENUM } from './common-const';
+import loginDevice from './system/login-device-const';
+import enterpriseConst from './business/oa/enterprise-const';
+import goodsConst from './business/erp/goods-const';
+import changeLogConst from './support/change-log-const';
+import fileConst from './support/file-const';
+import messageConst from "./support/message-const";
+
+export default {
+  FLAG_NUMBER_ENUM,
+  GENDER_ENUM,
+  USER_TYPE_ENUM,
+  ...loginDevice,
+  ...enterpriseConst,
+  ...goodsConst,
+  ...changeLogConst,
+  ...fileConst,
+  ...messageConst
+};

+ 19 - 0
src/constants/local-storage-key-const.js

@@ -0,0 +1,19 @@
+/*
+ *  key  常量
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-06 19:58:50
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+
+/**
+ * key前缀
+ */
+const KEY_PREFIX = 'smart_h5_';
+/**
+ * localStorageKey集合
+ */
+// token
+export const USER_TOKEN = `${KEY_PREFIX}token`;

+ 28 - 0
src/constants/regular-const.js

@@ -0,0 +1,28 @@
+/*
+ * 正则常量
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-06 19:59:05
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+export const regular = {
+  phone: /^(13|14|15|16|17|18|19)\d{9}$/,
+  qq: /^[1-9]\d{3,}$/,
+  linkUrl:
+    /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/,
+  // eslint-disable-next-line no-useless-escape
+  isNumber: /(^[\-1-9][1-9]*(.[1-9]+)?)$/, // 判断是否为数字,除了0 外
+  isLandlineOrPhone: /^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/, // 验证 座机 或者手机
+  account: /^[a-z0-9]{3,16}$/, // 请输入3-16位(小写字母|数字)的账号
+  mobileAccount: /^[a-z0-9]{6,16}$/, // 请输入6-16位(小写字母|数字)的账号(和移动端保持一致)
+  accountDesc: '请输入3-16位(小写字母|数字)的账号',
+  pwd: /^[A-Za-z0-9._]{6,16}$/, // 请输入6-16位(大小写字母|数字|小数点|下划线)的密码
+  pwdDesc: '请输入6-16位(大小写字母|数字|小数点|下划线)的密码',
+  delBlankSpace: /\s+/g, // 删除空格
+  isPdfReg: new RegExp(/\.(pdf|PDF)/),
+  isElseFileReg: new RegExp(/\.(doc|docx|xls|xlsx|txt|ppt|pptx|pps|ppxs)/),
+  isIdentityCard: /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X|x)$/, // 验证身份证号
+  isChinese: /^[\u4e00-\u9fa5]+$/gi, // 验证是否汉字
+};

+ 32 - 0
src/constants/support/change-log-const.js

@@ -0,0 +1,32 @@
+/**
+ * 系统更新日志 枚举
+ *
+ * @Author:    卓大
+ * @Date:      2022-09-26 14:53:50
+ * @Copyright  1024创新实验室
+ */
+
+/**
+ * 更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复]
+ */
+export const CHANGE_LOG_TYPE_ENUM = {
+  MAJOR_UPDATE: {
+    value: 1,
+    desc: '重大更新',
+    type: 'error',
+  },
+  FUNCTION_UPDATE: {
+    value: 2,
+    desc: '功能更新',
+    type: 'primary',
+  },
+  BUG_FIX: {
+    value: 3,
+    desc: 'Bug修复',
+    type: 'warning',
+  },
+};
+
+export default {
+  CHANGE_LOG_TYPE_ENUM,
+};

+ 31 - 0
src/constants/support/file-const.js

@@ -0,0 +1,31 @@
+/*
+ * 文件类型
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-03 22:09:10
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+// 文件上传类型
+export const FILE_FOLDER_TYPE_ENUM = {
+  COMMON: {
+    value: 1,
+    desc: '通用',
+  },
+  NOTICE: {
+    value: 2,
+    desc: '公告',
+  },
+  HELP_DOC: {
+    value: 3,
+    desc: '帮助中心',
+  },
+  FEEDBACK: {
+    value: 4,
+    desc: '意见反馈',
+  },
+};
+export default {
+  FILE_FOLDER_TYPE_ENUM,
+};

+ 30 - 0
src/constants/support/message-const.js

@@ -0,0 +1,30 @@
+/*
+ * @Description: file content
+ * @Author: yandy
+ * @Date: 2022-07-24 21:43:43
+ * @LastEditors:
+ * @LastEditTime: 2022-07-24 21:43:43
+ */
+export const MESSAGE_TYPE_ENUM = {
+    MAIL: {
+        value: 1,
+        desc: '站内信'
+    },
+    ORDER: {
+        value: 2,
+        desc: '订单'
+    },
+  };
+
+
+export const MESSAGE_RECEIVE_TYPE_ENUM = {
+    EMPLOYEE: {
+        value: 1,
+        desc: '员工'
+    },
+  };
+
+export default {
+    MESSAGE_TYPE_ENUM,
+    MESSAGE_RECEIVE_TYPE_ENUM
+};

+ 31 - 0
src/constants/system/login-device-const.js

@@ -0,0 +1,31 @@
+/*
+ * 登录设备
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-06 19:56:56
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+export const LOGIN_DEVICE_ENUM = {
+  PC: {
+    value: 1,
+    desc: '电脑端',
+  },
+  ANDROID: {
+    value: 2,
+    desc: '安卓',
+  },
+  APPLE: {
+    value: 3,
+    desc: '苹果',
+  },
+  H5: {
+    value: 3,
+    desc: 'H5',
+  },
+};
+
+export default {
+  LOGIN_DEVICE_ENUM,
+};

+ 120 - 0
src/lib/encrypt.js

@@ -0,0 +1,120 @@
+import CryptoJS from 'crypto-js';
+import CryptoSM from 'sm-crypto';
+
+function object2string(data) {
+  if (typeof data === 'object') {
+    return JSON.stringify(data);
+  }
+
+  let str = JSON.stringify(data);
+  if (str.startsWith("'") || str.startsWith('"')) {
+    str = str.substring(1);
+  }
+  if (str.endsWith("'") || str.endsWith('"')) {
+    str = str.substring(0, str.length - 1);
+  }
+  return str;
+}
+
+/**
+ * 字符串转为数字
+ */
+function stringToHex(str) {
+  let hex = '';
+  for(let i = 0; i < str.length; i++) {
+    hex += str.charCodeAt(i).toString(16).padStart(2, '0');
+  }
+  return hex;
+}
+
+
+/*
+ * -------------------- ※ AES 加密、解密 begin ※ --------------------
+ *
+ * 1、AES加密算法支持三种密钥长度:128位、192位和256位,这里选择128位
+ * 2、AES 要求秘钥为 128bit,转化字节为 16个字节;
+ * 3、js前端使用 UCS-2 或者 UTF-16 编码,字母、数字、特殊符号等 占用1个字节;
+ * 4、所以:秘钥Key 组成为:字母、数字、特殊符号 一共16个即可
+ *
+ * -------------------- ※ AES 加密、解密 end ※ --------------------
+ */
+const AES_KEY = '1024lab__1024lab';
+
+const AES = {
+  encryptData: function (data) {
+    // AES 加密 并转为 base64
+    let utf8Data = CryptoJS.enc.Utf8.parse(object2string(data));
+    const key = CryptoJS.enc.Utf8.parse(AES_KEY);
+    const encrypted = CryptoJS.AES.encrypt(utf8Data, key, {
+      mode: CryptoJS.mode.ECB,
+      padding: CryptoJS.pad.Pkcs7,
+    });
+    return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
+  },
+
+  decryptData: function (data) {
+    //  第一步:Base64 解码
+    let words = CryptoJS.enc.Base64.parse(data);
+
+    // 第二步:AES 解密
+    const key = CryptoJS.enc.Utf8.parse(AES_KEY);
+    return CryptoJS.AES.decrypt({ ciphertext: words }, key, {
+      mode: CryptoJS.mode.ECB,
+      padding: CryptoJS.pad.Pkcs7,
+    }).toString(CryptoJS.enc.Utf8);
+  },
+};
+
+/*
+ * -------------------- ※ 国密SM4算法 加密、解密 begin ※ --------------------
+ *
+ * 1、国密SM4 要求秘钥为 128bit,转化字节为 16个字节;
+ * 2、js前端使用 UCS-2 或者 UTF-16 编码,字母、数字、特殊符号等 占用1个字节;
+ * 3、java中 每个 字母数字 也是占用1个字节;
+ * 4、所以:前端和后端的 秘钥Key 组成为:字母、数字、特殊符号 一共16个即可
+ *
+ * -------------------- ※ 国密SM4算法 加密、解密 end ※ --------------------
+ */
+
+// 秘钥Key 组成为:字母、数字、特殊符号 一共16个即可
+const SM4_KEY = '1024lab__1024lab';
+
+const SM4 = {
+  encryptData: function (data) {
+    // 第一步:SM4 加密
+    let encryptData = CryptoSM.sm4.encrypt(object2string(data), stringToHex(SM4_KEY));
+    // 第二步: Base64 编码
+    return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encryptData));
+  },
+
+  decryptData: function (data) {
+    // 第一步:Base64 解码
+    let words = CryptoJS.enc.Base64.parse(data);
+    let decode64Str = CryptoJS.enc.Utf8.stringify(words);
+
+    // 第二步:SM4 解密
+    return CryptoSM.sm4.decrypt(decode64Str, stringToHex(SM4_KEY));
+  },
+};
+
+
+
+// -----------------------  对外暴露: 加密、解密 -----------------------
+
+// 默认使用SM4算法
+const EncryptObject = SM4;
+// const EncryptObject = AES;
+
+/**
+ * 加密
+ */
+export const encryptData = function (data) {
+  return !data ? null : EncryptObject.encryptData(data);
+};
+
+/**
+ * 解密
+ */
+export const decryptData = function (data) {
+  return !data ? null : EncryptObject.decryptData(data);
+};

+ 128 - 0
src/lib/smart-request.js

@@ -0,0 +1,128 @@
+/*
+ *  ajax请求
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-06 20:46:03
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+import { USER_TOKEN } from '@/constants/local-storage-key-const';
+import { DATA_TYPE_ENUM } from '@/constants/common-const';
+import { decryptData, encryptData } from './encrypt';
+import { useUserStore } from '@/store/modules/system/user';
+
+const baseUrl = import.meta.env.VITE_APP_API_URL;
+
+function getUserToken() {
+  let token = uni.getStorageSync(USER_TOKEN);
+  if (token) {
+    return token;
+  }
+  return '';
+}
+
+/**
+ * 处理返回的消息
+ */
+function handleResponse(response, resolve, reject) {
+  // 如果是加密数据
+  if (response.data.dataType === DATA_TYPE_ENUM.ENCRYPT.value) {
+    response.data.encryptData = response.data.data;
+    let decryptStr = decryptData(response.data.data);
+    if (decryptStr) {
+      response.data.data = JSON.parse(decryptStr);
+    }
+  }
+
+  const res = response.data;
+  if (res.code && res.code !== 1) {
+    // `token` 过期或者账号已在别处登录
+    if (res.code === 30007 || res.code === 30008 || res.code === 30012) {
+      uni.showToast({
+        title: res.msg,
+        icon: 'none',
+      });
+      useUserStore().clearUserLoginInfo();
+      uni.navigateTo({ url: '/pages/login/login' });
+    }
+
+    uni.showToast({
+      title: res.msg,
+      icon: 'none',
+    });
+    reject(response);
+  } else {
+    resolve(res);
+  }
+}
+
+/**
+ * 通用请求封装
+ */
+export const request = function (url, method, data) {
+  return new Promise((resolve, reject) => {
+    uni.request({
+      url: baseUrl + url, //拼接请求路径
+      data: data,
+      method: method,
+      header: {
+        'Authorization':'Bearer ' + getUserToken(),
+      },
+      success: (response) => {
+        handleResponse(response, resolve, reject);
+      },
+      fail: (error) => {
+        reject(error);
+      },
+    });
+  });
+};
+
+/**
+ * get请求
+ */
+export const getRequest = (url) => {
+  return request(url, 'GET');
+};
+
+/**
+ * post请求
+ */
+export const postRequest = (url, data) => {
+  return request(url, 'POST', data);
+};
+
+// ================================= 加密 =================================
+
+/**
+ * 加密请求参数的post请求
+ */
+export const postEncryptRequest = (url, data) => {
+  return request(url, 'POST', { encryptData: encryptData(data) });
+};
+
+// ================================= 文件 =================================
+
+export const uploadRequest = function (filePath, folder) {
+  return new Promise((resolve, reject) => {
+    uni.uploadFile({
+      url: baseUrl + '/support/file/upload',
+      filePath,
+      header: {
+        'Authorization':'Bearer ' +  getUserToken(),
+      },
+      name: 'file',
+      formData: {
+        folder,
+      },
+      success: (response) => {
+        response.data = JSON.parse(response.data.replace('\uFEFF', ''));
+        handleResponse(response, resolve, reject);
+      },
+      fail: (error) => {
+        reject(error);
+      },
+    });
+  });
+};

+ 22 - 0
src/lib/smart-sentry.js

@@ -0,0 +1,22 @@
+/*
+ * 错误上报sentry
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2024-01-02 20:49:28
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+
+export const smartSentry = {
+    /**
+     * sentry 主动上报
+     */
+    captureError: (error) => {
+        if (error.config && error.data && error && error.headers && error.request && error.status) {
+            return;
+        }
+        // Sentry.captureException(error);
+        console.error(error);
+    },
+};

+ 30 - 0
src/lib/smart-support.js

@@ -0,0 +1,30 @@
+export const SmartLoading = {
+  show: function (msg) {
+    uni.showLoading({ title: msg ? msg : '加载中' });
+  },
+
+  hide: function () {
+    uni.hideLoading();
+  },
+};
+
+export const SmartToast = {
+  success: (message) => {
+    uni.showToast({
+      title: message,
+      icon: 'success',
+    });
+  },
+  error: (message) => {
+    uni.showToast({
+      title: message,
+      icon: 'error',
+    });
+  },
+  toast: (message) => {
+    uni.showToast({
+      title: message,
+      icon: 'none',
+    });
+  },
+};

+ 22 - 0
src/main.js

@@ -0,0 +1,22 @@
+import { createSSRApp } from 'vue';
+import App from './App.vue';
+import { store } from './store/index';
+
+/*每个页面公共css */
+import './theme/index.scss';
+
+// 枚举管理
+import smartEnumPlugin from '@/plugins/smart-enums-plugin';
+import constantsInfo from '@/constants/index';
+import lodash from 'lodash';
+
+export function createApp() {
+  const app = createSSRApp(App);
+  app.use(store);
+  app.use(smartEnumPlugin, constantsInfo);
+  app.config.globalProperties.$lodash = lodash;
+  return {
+    app,
+    store,
+  };
+}

+ 72 - 0
src/manifest.json

@@ -0,0 +1,72 @@
+{
+    "name" : "smart-app",
+    "appid" : "",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx2f5032ef5c4adae4",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics": {  
+        "enable": false
+    },
+    "vueVersion" : "3"
+}

+ 212 - 0
src/pages.json

@@ -0,0 +1,212 @@
+{
+	"easycom": {
+		"autoscan": true,
+		"custom": {
+			"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
+			"^y-(.*)": "@/uni_modules/y-$1/components/y-$1.vue"
+		}
+	},
+	"pages": [
+		{
+			"path": "pages/home/index",
+			"style": {
+				"navigationStyle":"custom",
+				"navigationBarTitleText": "首页"
+			}
+		},
+		{
+			"path": "pages/login/login",
+			"style": {
+				"navigationBarTitleText": "登录",
+				"navigationBarTextStyle": "white",
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path" : "pages/mine/mine",
+			"style" : 
+			{
+				"navigationBarTitleText" : "我的",
+				"enablePullDownRefresh" : false,
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path" : "pages/enterprise/enterprise-list",
+			"style" : 
+			{
+				"navigationBarTitleText" : "客户线索",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/enterprise/enterprise-form",
+			"style" :
+			{
+				"navigationBarTitleText" : "添加客户",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/enterprise/enterprise-detail",
+			"style" :
+			{
+				"navigationBarTitleText" : "客户详情",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/notice/notice-index",
+			"style" :
+			{
+				"navigationBarTitleText" : "通知公告",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/notice/notice-detail",
+			"style" :
+			{
+				"navigationBarTitleText" : "公告内容",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/goods/goods-index",
+			"style" :
+			{
+				"navigationBarTitleText" : "商品",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/support/change-log/change-log-list",
+			"style" :
+			{
+				"navigationBarTitleText" : "版本更新",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/support/change-log/change-log-detail",
+			"style" :
+			{
+				"navigationBarTitleText" : "版本更新内容",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/message/message",
+			"style" : 
+			{
+				"navigationBarTitleText" : "消息",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/form/form",
+			"style" : 
+			{
+				"navigationBarTitleText" : "提交表单",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/select-people/select-people",
+			"style" : 
+			{
+				"navigationBarTitleText" : "选择人员",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/list/list",
+			"style" :
+			{
+				"navigationBarTitleText" : "常见列表样式1",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/list2/list",
+			"style" : 
+			{
+				"navigationBarTitleText" : "常见列表样式2",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/order-detail/order-detail",
+			"style" : 
+			{
+				"navigationBarTitleText" : "运单详情",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path" : "pages/support/feedback/feedback-form",
+			"style" :
+			{
+				"navigationBarTitleText" : "意见反馈",
+				"enablePullDownRefresh" : false,
+				"navigationBarBackgroundColor": "#fff"
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "uni-app",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#F8F8F8"
+	},
+	"tabBar": {
+		"color": "#858585",
+		"selectedColor": "#1A9AFF",
+		"borderStyle": "black",
+		"backgroundColor": "#ffffff",
+		"list": [{
+			"pagePath": "pages/home/index",
+			"iconPath": "static/images/tabbar/home-icon.png",
+			"selectedIconPath": "static/images/tabbar/home-icon-h.png",
+			"text": "首页"
+		},
+		{
+		  "pagePath": "pages/list/list",
+		  "iconPath": "static/images/tabbar/list-icon.png",
+		  "selectedIconPath": "static/images/tabbar/list-icon-h.png",
+		  "text": "常见列表1"
+		},
+		{
+		  "pagePath": "pages/list2/list",
+		  "iconPath": "static/images/tabbar/list-icon.png",
+		  "selectedIconPath": "static/images/tabbar/list-icon-h.png",
+		  "text": "常见列表2"
+		},
+		{
+		  "pagePath": "pages/message/message",
+		  "iconPath": "static/images/tabbar/message-icon.png",
+		  "selectedIconPath": "static/images/tabbar/message-icon-h.png",
+		  "text": "消息"
+		},
+		{
+			"pagePath": "pages/mine/mine",
+			"iconPath": "static/images/tabbar/mine-icon.png",
+			"selectedIconPath": "static/images/tabbar/mine-icon-h.png",
+			"text": "我的"
+		}]
+	}
+}

+ 245 - 0
src/pages/enterprise/enterprise-detail.vue

@@ -0,0 +1,245 @@
+<template>
+  <view class="container">
+    <view>
+      <smart-detail-tabs :tabsList="tabs" v-model="smartTabIndex" @change="onTabChange" :fixed="true" />
+    </view>
+    <view class="smart-detail smart-margin-top60 content" id="detail1">
+      <view class="smart-detail-card">
+        <view class="smart-detail-card-title"> 企业基础信息</view>
+        <view class="smart-detail-card-cell">
+          <view class="smart-detail-card-label"> 企业名称 </view>
+          <view class="smart-detail-card-value">
+            {{ data.enterpriseName }}
+          </view>
+        </view>
+        <view class="smart-detail-card-cell">
+          <view class="smart-detail-card-label"> 统一社会信用代码 </view>
+          <view class="smart-detail-card-value">
+            {{ data.unifiedSocialCreditCode }}
+          </view>
+        </view>
+        <view class="smart-detail-card-cell">
+          <view class="smart-detail-card-label"> 类型 </view>
+          <view class="smart-detail-card-value">
+            {{ $smartEnumPlugin.getDescByValue('ENTERPRISE_TYPE_ENUM', data.type) }}
+          </view>
+        </view>
+        <view class="smart-detail-card-cell">
+          <view class="smart-detail-card-label"> 企业ID </view>
+          <view class="smart-detail-card-value">
+            {{ data.enterpriseId }}
+          </view>
+        </view>
+      </view>
+
+      <view class="smart-detail-card" id="detail2">
+        <view class="smart-detail-card-title"> 图片信息</view>
+
+        <view class="smart-detail-card-cell" v-if="data.enterpriseLogo && data.enterpriseLogo.length > 0">
+          <view class="smart-detail-card-label"> 企业logo </view>
+          <view class="smart-detail-card-value" @click="preview([data.enterpriseLogo[0].fileUrl])">
+            <image :src="data.enterpriseLogo[0].fileUrl"></image>
+          </view>
+        </view>
+
+        <view class="smart-detail-card-cell" v-if="data.businessLicense && data.businessLicense.length > 0">
+          <view class="smart-detail-card-label"> 营业执照 </view>
+          <view class="smart-detail-card-value" @click="preview([data.businessLicense[0].fileUrl])">
+            <image :src="data.businessLicense[0].fileUrl"></image>
+          </view>
+        </view>
+      </view>
+
+      <view class="smart-detail-card" id="detail3">
+        <view class="smart-detail-card-title"> 联系方式</view>
+
+        <view class="smart-detail-card-cell">
+          <view class="smart-detail-card-label"> 联系人 </view>
+          <view class="smart-detail-card-value">
+            {{ data.contact }}
+          </view>
+        </view>
+
+        <view class="smart-detail-card-cell">
+          <view class="smart-detail-card-label"> 联系人电话 </view>
+          <view class="smart-detail-card-value">
+            {{ data.contactPhone }}
+          </view>
+        </view>
+        <view class="smart-detail-card-cell">
+          <view class="smart-detail-card-label"> 邮箱 </view>
+          <view class="smart-detail-card-value">
+            {{ data.email }}
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <view class="btn-group">
+      <button class="btn" type="warn" @click="onDelete">删除</button>
+      <button class="btn" type="primary" @click="onChange">修改</button>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import SmartDetailTabs from '@/components/smart-detail-tabs/index.vue';
+  import { ref, reactive } from 'vue';
+  import { enterpriseApi } from '@/api/business/oa/enterprise-api';
+  import { onShow, onLoad } from '@dcloudio/uni-app';
+  import { smartSentry } from '@/lib/smart-sentry';
+  import { SmartLoading, SmartToast } from '@/lib/smart-support';
+
+  // ----------------------- tab -----------------------
+
+  const tabs = ref([
+    {
+      label: '基础信息',
+      value: 1,
+    },
+    {
+      label: '图片信息',
+      value: 2,
+    },
+    {
+      label: '联系人',
+      value: 3,
+    },
+  ]);
+
+  const smartTabIndex = ref(1);
+
+  function onTabChange(tabIndex) {
+    console.log(12, tabIndex);
+    uni.pageScrollTo({
+      selector: '#detail' + tabIndex,
+      duration: 300,
+      success: console.log,
+      fail: console.log,
+    });
+  }
+
+  // ----------------------- 详情 -----------------------
+
+  const data = reactive({
+    //企业ID
+    enterpriseId: '',
+    //企业名称
+    enterpriseName: '',
+    //统一社会信用代码
+    unifiedSocialCreditCode: '',
+    //类型
+    type: '',
+    //联系人
+    contact: '',
+    //联系人电话
+    contactPhone: '',
+    //邮箱
+    email: '',
+    //详细地址
+    address: '',
+    //企业logo
+    enterpriseLogo: undefined,
+    //营业执照
+    businessLicense: undefined,
+  });
+
+  async function getDetail(id) {
+    try {
+      SmartLoading.show();
+      let res = await enterpriseApi.detail(id);
+      data.enterpriseId = res.data.enterpriseId;
+      data.enterpriseName = res.data.enterpriseName;
+      data.unifiedSocialCreditCode = res.data.unifiedSocialCreditCode;
+      data.type = res.data.type;
+      data.contact = res.data.contact;
+      data.contactPhone = res.data.contactPhone;
+      data.email = res.data.email;
+      data.address = res.data.address;
+      data.enterpriseLogo = res.data.enterpriseLogo;
+      data.businessLicense = res.data.businessLicense;
+    } catch (e) {
+      smartSentry.captureError(e);
+    } finally {
+      SmartLoading.hide();
+    }
+  }
+
+  function preview(urls) {
+    uni.previewImage({
+      urls: urls,
+    });
+  }
+
+  onLoad((options) => {
+    if (options.enterpriseId) {
+      data.enterpriseId = options.enterpriseId;
+      getDetail(options.enterpriseId);
+    }
+  });
+
+  onShow(() => {
+    getDetail(data.enterpriseId);
+  });
+
+  function onChange() {
+    uni.navigateTo({ url: '/pages/enterprise/enterprise-form?enterpriseId=' + data.enterpriseId });
+  }
+
+  function onDelete() {
+    uni.showModal({
+      title: '提示',
+      content: '确定要删除吗?',
+      confirmText: '确定删除',
+      success: function (res) {
+        if (res.confirm) {
+          doDelete();
+        }
+      },
+    });
+  }
+
+  async function doDelete() {
+    try {
+      SmartLoading.show();
+      await enterpriseApi.delete(data.enterpriseId);
+      SmartToast.success('删除成功');
+      setTimeout(() => {
+        uni.redirectTo({ url: '/pages/enterprise/enterprise-list' });
+      }, 500);
+    } catch (e) {
+      smartSentry.captureError(e);
+    } finally {
+      SmartLoading.hide();
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    background-color: #f4f4f4;
+    height: 100vh;
+    overflow-y: scroll;
+  }
+
+  .content {
+    margin-bottom: 120px;
+  }
+
+  .btn-group {
+    border-top: #eee 1px solid;
+    height: 80px;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    position: fixed;
+    bottom: 0;
+    background-color: white;
+    width: 100%;
+
+    .btn {
+      margin: 10px;
+      flex: 1;
+    }
+  }
+</style>

+ 184 - 0
src/pages/enterprise/enterprise-form.vue

@@ -0,0 +1,184 @@
+<template>
+  <view class="container">
+    <view class="smart-form">
+      <uni-forms ref="formRef" :label-width="100" :modelValue="form" label-position="left" :rules="rules">
+        <view class="smart-form-group">
+          <view class="smart-form-group-title"> 基本信息 </view>
+          <view class="smart-form-group-content">
+            <uni-forms-item class="smart-form-item" label="企业名称:" name="enterpriseName" required>
+              <uni-easyinput class="uni-mt-5" trim="all" v-model="form.enterpriseName" placeholder="请输入 企业名称" />
+            </uni-forms-item>
+            <uni-forms-item class="smart-form-item" label="统一社会信用代码:" name="unifiedSocialCreditCode" required>
+              <uni-easyinput class="uni-mt-5" trim="all" v-model="form.unifiedSocialCreditCode" placeholder="请输入 统一社会信用代码" />
+            </uni-forms-item>
+            <uni-forms-item class="smart-form-item" label="企业类型:" name="type" required>
+              <smart-enum-radio v-model="form.type" enumName="ENTERPRISE_TYPE_ENUM" />
+            </uni-forms-item>
+
+            <uni-forms-item class="smart-form-item" label="公司地址:" name="address">
+              <uni-easyinput class="uni-mt-5" trim="all" v-model="form.address" placeholder="请输入 公司地址" />
+            </uni-forms-item>
+          </view>
+        </view>
+
+        <view class="smart-form-group">
+          <view class="smart-form-group-title"> 联系方式 </view>
+          <view class="smart-form-group-content">
+            <uni-forms-item class="smart-form-item" label="联系人" name="contact" required>
+              <uni-easyinput class="uni-mt-5" trim="all" v-model="form.contact" placeholder="请输入 联系人" />
+            </uni-forms-item>
+            <uni-forms-item class="smart-form-item" label="联系人电话" name="contactPhone" required>
+              <uni-easyinput class="uni-mt-5" trim="all" v-model="form.contactPhone" placeholder="请输入 联系人电话" />
+            </uni-forms-item>
+            <uni-forms-item class="smart-form-item" label="邮箱" name="email">
+              <uni-easyinput class="uni-mt-5" trim="all" v-model="form.email" placeholder="请输入 邮箱" />
+            </uni-forms-item>
+          </view>
+        </view>
+      </uni-forms>
+
+      <view class="smart-form-submit smart-margin-top20 bottom-button">
+        <button class="smart-form-submit-btn smart-margin-right20" type="default" @click="cancel">取消</button>
+        <button class="smart-form-submit-btn" type="warn" @click="reset">重置</button>
+        <button class="smart-form-submit-btn" type="primary" @click="ok">保存</button>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import { reactive, ref } from 'vue';
+  import { enterpriseApi } from '@/api/business/oa/enterprise-api';
+  import { regular } from '@/constants/regular-const';
+  import SmartEnumRadio from '@/components/smart-enum-radio/index.vue';
+  import { smartSentry } from '@/lib/smart-sentry';
+  import { SmartLoading, SmartToast } from '@/lib/smart-support';
+  import { onLoad, onReady, onShow } from '@dcloudio/uni-app';
+
+  // --------------------- 表单 ---------------------
+
+  const defaultFormData = {
+    enterpriseId: undefined,
+    enterpriseName: undefined,
+    unifiedSocialCreditCode: undefined,
+    type: undefined,
+    contact: undefined,
+    contactPhone: undefined,
+    email: undefined,
+    address: undefined,
+    disabledFlag: false,
+  };
+  let form = reactive({ ...defaultFormData });
+
+  const rules = {
+    enterpriseName: {
+      rules: [{ required: true, errorMessage: '请输入企业名称' }],
+    },
+    unifiedSocialCreditCode: {
+      rules: [{ required: true, errorMessage: '请输入 统一社会信用代码' }],
+    },
+    contact: {
+      rules: [{ required: true, errorMessage: '请输入 联系人' }],
+    },
+    contactPhone: {
+      rules: [
+        { required: true, errorMessage: '请输入联系人电话' },
+        { pattern: regular.phone, errorMessage: '电话格式错误' },
+      ],
+    },
+    type: {
+      rules: [{ required: true, errorMessage: '请选择 类型' }],
+    },
+  };
+
+  // --------------------- 详情 ---------------------
+
+  onLoad((options) => {
+    if (options.enterpriseId) {
+      form.enterpriseId = options.enterpriseId;
+      getDetail(options.enterpriseId);
+    }
+  });
+
+  onReady(() => {
+    if (form.enterpriseId) {
+      uni.setNavigationBarTitle({
+        title: '修改客户',
+      });
+    }
+  });
+
+  async function getDetail(id) {
+    try {
+      SmartLoading.show();
+      let res = await enterpriseApi.detail(id);
+      form.enterpriseId = res.data.enterpriseId;
+      form.enterpriseName = res.data.enterpriseName;
+      form.unifiedSocialCreditCode = res.data.unifiedSocialCreditCode;
+      form.type = res.data.type;
+      form.contact = res.data.contact;
+      form.contactPhone = res.data.contactPhone;
+      form.email = res.data.email;
+      form.address = res.data.address;
+    } catch (e) {
+      smartSentry.captureError(e);
+    } finally {
+      SmartLoading.hide();
+    }
+  }
+
+  // ----------------------- 表单操作 ------------------------
+
+  const formRef = ref();
+
+  // 取消
+  function cancel() {
+    close();
+    uni.navigateBack();
+  }
+
+  // 重置
+  function reset() {
+    Object.assign(form, defaultFormData);
+    formRef.value.clearValidate();
+  }
+
+  // 确定
+  function ok() {
+    formRef.value
+      .validate()
+      .then(async () => {
+        SmartLoading.show();
+        try {
+          if (form.enterpriseId) {
+            await enterpriseApi.update(form);
+          } else {
+            await enterpriseApi.create(form);
+          }
+          SmartToast.success(`${form.enterpriseId ? '修改' : '添加'}成功`);
+
+          uni.navigateBack();
+        } catch (error) {
+          smartSentry.captureError(error);
+        } finally {
+          SmartLoading.hide();
+        }
+      })
+      .catch((error) => {
+        console.log('error', error);
+        SmartToast.toast('参数验证错误,请仔细填写表单数据!');
+      });
+  }
+</script>
+
+<style lang="scss" scoped>
+  .query-form-pop {
+    height: 800rpx;
+    overflow-y: scroll;
+  }
+
+  .bottom-button {
+    position: fixed;
+    bottom: 0;
+  }
+</style>

+ 217 - 0
src/pages/enterprise/enterprise-list.vue

@@ -0,0 +1,217 @@
+<template>
+  <view class="container">
+    <mescroll-body @init="mescrollInit" :down="{ auto: false }" @down="onDown" @up="onUp" :up="{ toTop: { src: '' } }">
+      <!--搜索框-->
+      <uni-nav-bar :border="false" fixed :leftWidth="0" rightWidth="70px">
+        <view class="input">
+          <uni-easyinput
+            prefixIcon="search"
+            :clearable="true"
+            trim="all"
+            v-model="queryForm.keywords"
+            placeholder="搜索:企业名称、联系人、联系电话等"
+            @confirm="search"
+            @clear="search"
+          />
+        </view>
+        <template #right>
+          <view class="nav-right" @click="search">
+            <uni-icons type="search" size="30"></uni-icons>
+            <view class="nav-right-name"> 搜索 </view>
+          </view>
+        </template>
+      </uni-nav-bar>
+
+      <!-- 列表 -->
+      <view class="list-container">
+        <view class="list-item" @click="gotoDetail(item.enterpriseId)" v-for="item in listData" :key="item.enterpriseId">
+          <view class="list-item-row">
+            <view class="list-item-label">公司:</view>
+            <view class="list-item-content bolder">{{ item.enterpriseName }}</view>
+            <view class="list-item-phone" @click="callPhone(item.contactPhone)">
+              <uni-icons type="phone" size="18" color="#007aff"></uni-icons>
+              联系
+            </view>
+          </view>
+          <view class="list-item-row">
+            <view class="list-item-label">营业执照:</view>
+            <view class="list-item-content">{{ item.unifiedSocialCreditCode }}</view>
+          </view>
+          <view class="list-item-row">
+            <view class="list-item-label">联系人:</view>
+            <view class="list-item-content">{{ item.contact }}</view>
+          </view>
+          <view class="list-item-row">
+            <view class="list-item-label">电话:</view>
+            <view class="list-item-content">{{ item.contactPhone }}</view>
+          </view>
+        </view>
+      </view>
+    </mescroll-body>
+
+    <uni-fab ref="fab" :pattern="fabPattern" horizontal="right" @fabClick="gotoAdd" />
+  </view>
+</template>
+
+<script setup>
+  import { reactive, ref } from 'vue';
+  import { enterpriseApi } from '@/api/business/oa/enterprise-api';
+  import { onPageScroll, onReachBottom, onShow } from '@dcloudio/uni-app';
+  import useMescroll from '@/uni_modules/uni-mescroll/hooks/useMescroll';
+  import { smartSentry } from '@/lib/smart-sentry';
+  import _ from 'lodash';
+
+  // --------------------------- 查询 ---------------------------------
+
+  const defaultForm = {
+    keywords: '', //标题、作者、来源
+    pageNum: 1,
+    pageSize: 10,
+  };
+
+  // 查询表单
+  const queryForm = reactive({ ...defaultForm });
+  // 列表数据
+  const listData = ref([]);
+
+  function buildQueryParam(pageNum) {
+    queryForm.pageNum = pageNum;
+    return Object.assign({}, queryForm, { pageNum });
+  }
+
+  async function query(mescroll, isDownFlag, param) {
+    try {
+      let res = await enterpriseApi.pageQuery(param);
+      if (!isDownFlag) {
+        listData.value = listData.value.concat(res.data.list);
+      } else {
+        listData.value = res.data.list;
+      }
+      mescroll.endSuccess(res.data.list.length, res.data.pages > res.data.pageNum);
+    } catch (e) {
+      smartSentry.captureError(e);
+      //联网失败, 结束加载
+      mescroll.endErr();
+    }
+  }
+
+  const { mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
+
+  /**
+   * 搜索
+   */
+  function search() {
+    query(getMescroll(), true, buildQueryParam(1));
+    uni.pageScrollTo({
+      scrollTop: 0,
+    });
+  }
+
+  /**
+   * 下拉刷新
+   */
+  function onDown(mescroll) {
+    queryForm.pageNum = 1;
+    query(mescroll, true, buildQueryParam(1));
+  }
+
+  /**
+   * 上拉加载更多
+   */
+  function onUp(mescroll) {
+    query(mescroll, false, buildQueryParam(mescroll.num));
+  }
+
+  onShow(() => {
+    search();
+  });
+
+  // --------------------------- 添加 ---------------------------------
+
+  const fabPattern = reactive({
+    color: 'blue',
+    backgroundColor: 'blue',
+  });
+
+  function gotoAdd() {
+    uni.navigateTo({ url: '/pages/enterprise/enterprise-form' });
+  }
+
+  // --------------------------- 详情 ---------------------------------
+
+  function callPhone(phone) {
+    uni.makePhoneCall({
+      phoneNumber: phone, //仅为示例
+    });
+  }
+  function gotoDetail(id) {
+    uni.navigateTo({ url: '/pages/enterprise/enterprise-detail?enterpriseId=' + id });
+  }
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    background-color: #f4f4f4;
+  }
+  .input {
+    width: 100%;
+    height: 60rpx;
+    background: #f7f8f9;
+    border-radius: 4px;
+    margin: 8rpx 0;
+    display: flex;
+    align-items: center;
+  }
+
+  .nav-right {
+    width: 140rpx;
+    display: flex;
+    height: 88rpx;
+    flex-direction: row;
+    line-height: 88rpx;
+    .nav-right-name {
+      margin-left: 5px;
+      line-height: 88rpx;
+      font-size: 30rpx;
+    }
+  }
+
+  .list-container {
+    padding: 10rpx 20rpx;
+    margin-top: 10rpx;
+
+    .list-item {
+      background: #ffffff;
+      box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+      margin-bottom: 20rpx;
+      padding: 30rpx 40rpx;
+
+      .list-item-row {
+        display: flex;
+        flex-direction: row;
+        margin-bottom: 16rpx;
+        justify-content: flex-start;
+
+        .list-item-label {
+          font-size: 28rpx;
+          font-weight: 400;
+          text-align: left;
+          color: #999999;
+        }
+        .bolder {
+          font-weight: 600 !important;
+        }
+        .list-item-content {
+          font-size: 30rpx;
+          font-weight: 500;
+          text-align: left;
+          color: #323333;
+        }
+        .list-item-phone {
+          color: $uni-color-primary;
+          margin-left: auto;
+        }
+      }
+    }
+  }
+</style>

+ 94 - 0
src/pages/form/form.vue

@@ -0,0 +1,94 @@
+<template>
+  <view class="smart-form">
+    <uni-forms :label-width="100" :modelValue="formData" label-position="left">
+      <view class="smart-form-group">
+        <view class="smart-form-group-title"> 常用功能 </view>
+        <view class="smart-form-group-content">
+          <uni-forms-item class="smart-form-item" label="姓名" name="name" required>
+            <uni-easyinput trim="all" v-model="formData.name" placeholder="请输入姓名" />
+          </uni-forms-item>
+          <uni-forms-item class="smart-form-item" label="手机号码" name="name" required>
+            <uni-easyinput trim="all" v-model="formData.name" placeholder="请输入手机号码" />
+          </uni-forms-item>
+          <uni-forms-item class="smart-form-item" label="邮箱地址" name="name">
+            <uni-easyinput trim="all" v-model="formData.name" placeholder="请输入邮箱地址" />
+          </uni-forms-item>
+          <uni-forms-item class="smart-form-item" label="性别" required>
+            <uni-data-checkbox v-model="formData.sex" :localdata="sexs" />
+          </uni-forms-item>
+          <uni-forms-item class="smart-form-item" label="出生日期" name="name">
+            <uni-datetime-picker type="date" return-type="timestamp" v-model="formData.datetimesingle" />
+          </uni-forms-item>
+        </view>
+      </view>
+      <view class="smart-form-group">
+        <view class="smart-form-group-title"> 兴趣爱好 </view>
+        <view class="smart-form-group-content">
+          <uni-forms-item class="smart-form-item" label="兴趣爱好" name="interest">
+            <uni-data-checkbox mode="button" multiple v-model="formData.interest" :localdata="interestList"></uni-data-checkbox>
+          </uni-forms-item>
+        </view>
+      </view>
+      <view class="smart-form-group">
+        <view class="smart-form-group-title"> 屏幕设置 </view>
+        <view class="smart-form-group-content">
+          <uni-forms-item class="smart-form-item" label="亮度调整" name="name">
+            <slider style="width: 100%" value="50" activeColor="#2291F9" backgroundColor="#f5f6f8" block-color="#2291F9" block-size="20" />
+          </uni-forms-item>
+        </view>
+      </view>
+      <view class="smart-form-group">
+        <view class="smart-form-group-title"> 自我介绍 </view>
+        <view class="smart-form-group-content">
+          <uni-forms-item class="smart-form-item" label="兴趣爱好" name="interest">
+            <uni-easyinput type="textarea" autoHeight v-model="value" placeholder="请输入自我介绍" />
+          </uni-forms-item>
+
+          <uni-forms-item class="smart-form-item" label="上传图片" name="interest">
+            <uni-file-picker limit="9" title="最多选择9张图片" />
+          </uni-forms-item>
+        </view>
+      </view>
+    </uni-forms>
+    <view class="smart-form-submit fixed-bottom-button">
+      <button class="smart-form-submit-btn smart-margin-right20" type="default">取消</button>
+      <button class="smart-form-submit-btn" type="warn">重置</button>
+      <button class="smart-form-submit-btn" type="primary">确定</button>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import { reactive } from 'vue';
+
+  const sexs = [
+    {
+      text: '男',
+      value: 0,
+    },
+    {
+      text: '女',
+      value: 1,
+    },
+    {
+      text: '你懂的',
+      value: 2,
+    },
+  ];
+
+  const interestList = [
+    { text: '唱歌', value: 1 },
+    { text: '足球', value: 2 },
+    { text: '篮球', value: 3 },
+    { text: '跑步', value: 4 },
+    { text: '写字', value: 5 },
+    { text: '美术', value: 6 },
+    { text: '射击', value: 7 },
+    { text: '健身', value: 8 },
+    { text: '马术', value: 9 },
+    { text: '美食', value: 10 },
+  ];
+  const formData = reactive({});
+</script>
+
+<style lang="scss" scoped></style>

+ 68 - 0
src/pages/goods/components/goods-list.vue

@@ -0,0 +1,68 @@
+<template>
+  <view class="list-container">
+    <view class="list-item" @click="gotoDetail(item.goodsId)" v-for="item in list" :key="item.noticeId">
+      <view class="title"> {{ item.goodsName }} </view>
+      <view class="author" v-if="item.remark"> 备注:{{ item.remark }} </view>
+      <view class="author" v-if="item.categoryName"> 分类:{{ item.categoryName }} </view>
+      <view class="author"> 创建时间:{{ item.createTime }} </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  const props = defineProps({
+    marginTop: {
+      type: String,
+      default: '0',
+    },
+    list: {
+      type: Array,
+      default: [],
+    },
+  });
+
+  function gotoDetail(goodsId) {
+    uni.navigateTo({ url: '/pages/goods/goods-detail?noticeId=' + goodsId });
+  }
+</script>
+
+<style lang="scss" scoped>
+  .list-container {
+    margin-top: v-bind(marginTop);
+    padding: 20rpx 10rpx;
+  }
+  .list-item {
+    box-sizing: border-box;
+    margin: 0 30rpx;
+    padding: 30rpx 0;
+    border-bottom: #cdcdcd 1px solid;
+
+    .title {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 20rpx;
+      font-size: 16px;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      overflow: hidden;
+    }
+    .author {
+      margin-bottom: 14rpx;
+      color: #999999;
+      font-size: 12px;
+      text-overflow: ellipsis;
+    }
+    .publish-time {
+      font-size: 12px;
+      color: #999999;
+      display: flex;
+      flex-direction: row;
+      justify-content: space-between;
+
+      .un-read {
+        color: $uni-color-primary;
+      }
+    }
+  }
+</style>

+ 100 - 0
src/pages/goods/components/goods-query-form-popup.vue

@@ -0,0 +1,100 @@
+<template>
+  <uni-popup ref="popupRef" background-color="#fff" type="bottom" :is-mask-click="false">
+    <view class="query-form-pop">
+      <view class="smart-form">
+        <uni-forms :label-width="100" :modelValue="form" label-position="left">
+          <view class="smart-form-group">
+            <view class="smart-form-group-title"> 产地 </view>
+            <view class="smart-form-group-content">
+              <DictSelect keyCode="GODOS_PLACE" v-model="form.place" @change="onPlaceChange" />
+            </view>
+          </view>
+
+          <view class="smart-form-group">
+            <view class="smart-form-group-title"> 状态 </view>
+            <view class="smart-form-group-content">
+              <SmartEnumSelect enumName="GOODS_STATUS_ENUM" @change="onGoodsStatusChange" v-model="form.goodsStatus" />
+            </view>
+          </view>
+        </uni-forms>
+
+        <view class="smart-form-submit smart-margin-top20">
+          <button class="smart-form-submit-btn smart-margin-right20" type="default" @click="cancel">取消</button>
+          <button class="smart-form-submit-btn" type="warn" @click="reset">重置</button>
+          <button class="smart-form-submit-btn" type="primary" @click="ok">确定</button>
+        </view>
+      </view>
+    </view>
+  </uni-popup>
+</template>
+
+<script setup>
+  import { reactive, ref, toRaw } from 'vue';
+  import DictSelect from '@/components/dict-select/index.vue';
+  import SmartEnumSelect from '@/components/smart-enum-select/index.vue';
+
+  const emits = defineEmits(['close']);
+  defineExpose({ show });
+
+  // --------------------- 显示 隐藏 ---------------------
+  const popupRef = ref();
+
+  function show() {
+    popupRef.value.open();
+  }
+
+  function close() {
+    popupRef.value.close();
+  }
+
+  // --------------------- 数据变化 ---------------------
+
+  function onPlaceChange(value, text) {
+    form.placeName = text;
+  }
+
+  function onGoodsStatusChange(value, text) {
+    form.goodsStatusName = text;
+  }
+
+  // --------------------- 表单 ---------------------
+
+  const defaultFormData = {
+    // 商品状态
+    goodsStatus: undefined,
+    goodsStatusName: undefined,
+    // 产地
+    place: undefined,
+    placeName: undefined,
+  };
+
+  const form = reactive({ ...defaultFormData });
+
+  // ----------------------- 表单操作 ------------------------
+
+  // 取消
+  function cancel() {
+    close();
+    emits('close', null);
+  }
+
+  // 重置
+  function reset() {
+    Object.assign(form, defaultFormData);
+    close();
+    emits('close', toRaw(form));
+  }
+
+  // 确定
+  function ok() {
+    close();
+    emits('close', toRaw(form));
+  }
+</script>
+
+<style lang="scss" scoped>
+  .query-form-pop {
+    height: 800rpx;
+    overflow-y: scroll;
+  }
+</style>

+ 222 - 0
src/pages/goods/goods-index.vue

@@ -0,0 +1,222 @@
+<template>
+  <view>
+    <mescroll-body @init="mescrollInit" :down="{ auto: false }" @down="onDown" @up="onUp">
+      <!--搜索框-->
+      <uni-nav-bar :border="false" fixed :leftWidth="0" rightWidth="70px">
+        <view class="input">
+          <uni-easyinput
+            prefixIcon="search"
+            :clearable="true"
+            trim="all"
+            v-model="queryForm.searchWord"
+            placeholder="搜索:商品名称"
+            @confirm="search"
+            @clear="search"
+          />
+        </view>
+        <template #right>
+          <view class="nav-right" @click="showQueryFormPopUp">
+            <uni-icons type="settings" size="30"></uni-icons>
+            <view class="nav-right-name"> 筛选 </view>
+          </view>
+        </template>
+      </uni-nav-bar>
+
+      <!--筛选条件提示-->
+      <uni-notice-bar
+        @close="onCloseQueryFormTips"
+        v-if="showQueryFormTipsFlag"
+        class="query-bar"
+        background-color="#007aff"
+        color="white"
+        show-close
+        single
+        :text="queryFormTips"
+      />
+
+      <!-- 筛选条件表单弹窗 -->
+      <QueryFormPopUp ref="queryFormPopUpRef" @close="onCloseQueryFormPopUp" />
+
+      <!-- 列表 -->
+      <NoticeList :list="listData" :margin-top="queryFormTipsMarginTop" />
+    </mescroll-body>
+  </view>
+</template>
+
+<script setup>
+  import { reactive, ref } from 'vue';
+  import QueryFormPopUp from './components/goods-query-form-popup.vue';
+  import { goodsApi } from '@/api/business/goods/goods-api';
+  import { onPageScroll, onReachBottom } from '@dcloudio/uni-app';
+  import useMescroll from '@/uni_modules/uni-mescroll/hooks/useMescroll';
+  import { smartSentry } from '@/lib/smart-sentry';
+  import NoticeList from './components/goods-list.vue';
+  import _ from 'lodash';
+
+  // --------------------------- 筛选条件弹窗 ---------------------------------
+  const queryFormPopUpRef = ref();
+
+  /**
+   * 显示 筛选弹窗
+   */
+  function showQueryFormPopUp() {
+    queryFormPopUpRef.value.show();
+  }
+
+  /**
+   * 监听 筛选弹窗 关闭
+   */
+  function onCloseQueryFormPopUp(param) {
+    if (param === null) {
+      return;
+    }
+    Object.assign(queryForm, param);
+    showOrHideQueryFormTips();
+    query(getMescroll(), true, buildQueryParam(1));
+    uni.pageScrollTo({
+      scrollTop: 0,
+    });
+  }
+
+  // --------------------------- 筛选条件tips ---------------------------------
+  const queryFormTips = ref(null);
+  const showQueryFormTipsFlag = ref(false);
+  const queryFormTipsMarginTop = ref('0px');
+
+  /**
+   * 查询提示
+   */
+  function buildQueryFormTips() {
+    let tips = null;
+    if (queryForm.searchWord) {
+      tips = '搜索:' + queryForm.searchWord;
+    }
+    if (queryForm.goodsStatusName) {
+      tips = tips ? tips + ',' : '';
+      tips = tips + '商品状态:' + queryForm.goodsStatusName;
+    }
+    if (queryForm.placeName) {
+      tips = tips ? tips + ',' : '';
+      tips = tips + '产地:' + queryForm.placeName;
+    }
+    return tips;
+  }
+
+  /**
+   * 显示或者隐藏tips
+   */
+  function showOrHideQueryFormTips() {
+    let tips = buildQueryFormTips();
+    queryFormTipsMarginTop.value = _.isEmpty(tips) ? '0px' : '50rpx';
+    showQueryFormTipsFlag.value = !_.isEmpty(tips);
+    queryFormTips.value = tips;
+  }
+
+  /**
+   * 关闭筛选条件 tips,清空搜索条件
+   */
+  function onCloseQueryFormTips() {
+    Object.assign(queryForm, defaultForm);
+    queryFormTipsMarginTop.value = '0px';
+    showQueryFormTipsFlag.value = false;
+    queryFormTips.value = '';
+    search();
+  }
+
+  // --------------------------- 查询 ---------------------------------
+
+  const defaultForm = {
+    searchWord: '',
+    // 商品状态
+    goodsStatus: undefined,
+    goodsStatusName: undefined,
+    // 产地
+    place: undefined,
+    placeName: undefined,
+    pageNum: 1,
+    pageSize: 10,
+  };
+
+  // 查询表单
+  const queryForm = reactive({ ...defaultForm });
+  // 列表数据
+  const listData = ref([]);
+
+  function buildQueryParam(pageNum) {
+    queryForm.pageNum = pageNum;
+    return Object.assign({}, queryForm, { pageNum });
+  }
+
+  async function query(mescroll, isDownFlag, param) {
+    try {
+      let res = await goodsApi.queryGoodsList(param);
+      if (!isDownFlag) {
+        listData.value = listData.value.concat(res.data.list);
+      } else {
+        listData.value = res.data.list;
+      }
+      mescroll.endSuccess(res.data.list.length, res.data.pages > res.data.pageNum);
+    } catch (e) {
+      smartSentry.captureError(e);
+      //联网失败, 结束加载
+      mescroll.endErr();
+    }
+  }
+
+  const { mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
+
+  /**
+   * 搜索
+   */
+  function search() {
+    showOrHideQueryFormTips();
+    query(getMescroll(), true, buildQueryParam(1));
+    uni.pageScrollTo({
+      scrollTop: 0,
+    });
+  }
+
+  /**
+   * 下拉刷新
+   */
+  function onDown(mescroll) {
+    queryForm.pageNum = 1;
+    query(mescroll, true, buildQueryParam(1));
+  }
+
+  /**
+   * 上拉加载更多
+   */
+  function onUp(mescroll) {
+    query(mescroll, false, buildQueryParam(mescroll.num));
+  }
+</script>
+
+<style lang="scss" scoped>
+  .input {
+    width: 100%;
+    height: 72rpx;
+    background: #f7f8f9;
+    border-radius: 4px;
+    margin: 8rpx 0;
+    display: flex;
+    align-items: center;
+  }
+
+  .nav-right {
+    width: 140px;
+    display: flex;
+    height: 88rpx;
+    flex-direction: row;
+    line-height: 88rpx;
+    .nav-right-name {
+      margin-left: 5px;
+      line-height: 88rpx;
+      font-size: 30rpx;
+    }
+  }
+
+  .query-bar {
+    position: fixed;
+  }
+</style>

+ 44 - 0
src/pages/home/components/banner.vue

@@ -0,0 +1,44 @@
+<template>
+  <view class="banner" v-if="!$lodash.isEmpty(bannerList)">
+    <swiper class="swiper" circular :indicator-dots="true" :autoplay="true" indicator-active-color="#fff">
+      <swiper-item v-for="(item, index) in bannerList" :key="index">
+        <image class="swiper-item" :src="item.image" mode=""></image>
+      </swiper-item>
+    </swiper>
+  </view>
+</template>
+
+<script setup>
+  import { ref } from 'vue';
+  const bannerList = ref([
+    {
+      image: '/static/images/index/banner.png',
+    },
+    {
+      image: '/static/images/index/banner.png',
+    },
+    {
+      image: '/static/images/index/banner.png',
+    },
+    {
+      image: '/static/images/index/banner.png',
+    },
+  ]);
+</script>
+
+<style lang="scss" scoped>
+  .banner {
+    border-radius: 16rpx;
+    overflow: hidden;
+    width: 700rpx;
+    height: 300rpx;
+    margin: 10rpx auto 20rpx;
+    .swiper {
+      width: 100%;
+      .swiper-item {
+        width: 100%;
+        height: 100%;
+      }
+    }
+  }
+</style>

+ 135 - 0
src/pages/home/components/goods.vue

@@ -0,0 +1,135 @@
+<template>
+  <view class="container">
+    <uni-card title="品质商品" :isFull="true" padding="0px" spacing="0px">
+      <view class="course">
+        <scroll-view class="scroll-view" scroll-x="true">
+          <view class="scroll-view-item" v-for="item in goodsList" :key="item.goodsId">
+            <div style="display: flex">
+              <image class="scroll-item-left" src="/static/logo.png" mode=""></image>
+              <view class="scroll-item-right">
+                <view class="item-title"> {{ item.categoryName }}-{{ item.goodsName }} </view>
+                <view class="item-right-bottom">
+                  <view class="item-time"> 产地:{{ item.place[0].valueName }}</view>
+                </view>
+                <view class="item-right-bottom">
+                  <view class="item-time"> {{ item.remark }}</view>
+                  <view class="item-reserved"> 预约 </view>
+                </view>
+              </view>
+            </div>
+          </view>
+        </scroll-view>
+      </view>
+    </uni-card>
+  </view>
+</template>
+
+<script setup>
+  import { ref } from 'vue';
+  import { smartSentry } from '@/lib/smart-sentry';
+  import { onShow } from '@dcloudio/uni-app';
+  import { goodsApi } from '@/api/business/goods/goods-api';
+
+  const queryForm = {
+    pageNum: 1,
+    pageSize: 5,
+    searchCount: false,
+  };
+
+  const goodsList = ref([]);
+  async function queryGoodsList() {
+    try {
+      const result = await goodsApi.queryGoodsList(queryForm);
+      goodsList.value = result.data.list;
+    } catch (err) {
+      smartSentry.captureError(err);
+    }
+  }
+
+  onShow(() => {
+    queryGoodsList();
+  });
+
+  // 查看更多
+  function onMore() {
+    router.push({
+      path: '/oa/notice/notice-employee-list',
+    });
+  }
+
+  function toDetail(noticeId) {
+    router.push({
+      path: '/oa/notice/notice-employee-detail',
+      query: { noticeId },
+    });
+  }
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    width: 700rpx;
+    margin: 0 auto 20rpx;
+    border-radius: 12rpx;
+    overflow: hidden;
+    :deep(.uni-card__header-box) {
+      font-weight: bold;
+    }
+    :deep(.uni-card__content) {
+      padding: 0 !important;
+    }
+    :deep(.uni-card__header) {
+      background: linear-gradient(180deg, #e8f4ff, #f8fcff);
+    }
+  }
+
+  .course {
+    padding: 30rpx 0 30rpx 30rpx;
+    background-color: #fff;
+  }
+  .scroll-view-item {
+    display: inline-block;
+    width: 570rpx;
+    height: 128rpx;
+    overflow: hidden;
+    font-size: 36rpx;
+    border-right: 1rpx solid #ededed;
+    margin-right: 30rpx;
+    .scroll-item-left {
+      width: 128rpx;
+      height: 128rpx;
+      margin-right: 20rpx;
+    }
+
+    .scroll-item-right {
+      .item-title {
+        white-space: pre-wrap;
+        font-size: 30rpx;
+        width: 390rpx;
+      }
+
+      .item-right-bottom {
+        display: flex;
+        justify-content: space-between;
+
+        .item-time {
+          font-size: 12px;
+          font-weight: 400;
+          color: #777777;
+          letter-spacing: -0.02px;
+        }
+
+        .item-reserved {
+          width: 100rpx;
+          height: 40rpx;
+          background: #1a9aff;
+          border-radius: 4px;
+          box-shadow: 0 5rpx 8rpx 0 rgba(58, 121, 255, 0.2);
+          color: #fff;
+          text-align: center;
+          font-size: 26rpx;
+          line-height: 40rpx;
+        }
+      }
+    }
+  }
+</style>

+ 131 - 0
src/pages/home/components/menu.vue

@@ -0,0 +1,131 @@
+<template>
+  <view class="menu-container">
+    <uni-grid :column="5" :highlight="true" :show-border="false" customStyle="display: block;">
+      <!--------------------------------- 第一排--------------------------------->
+      <uni-grid-item class="menu-grid" style="width: 134rpx; height: 134rpx">
+        <view class="menu-item" @click="changeHome">
+          <image class="item-image" src="@/static/images/index/ic_home_menu1.png"></image>
+          <view class="item-text"> 首页切换 </view>
+        </view>
+      </uni-grid-item>
+
+      <uni-grid-item class="menu-grid" style="width: 134rpx; height: 134rpx">
+        <view class="menu-item" @click="navigateTo('/pages/notice/notice-index')">
+          <image class="item-image" src="@/static/images/index/ic_home_menu2.png"></image>
+          <view class="item-text"> 通知公告 </view>
+        </view>
+      </uni-grid-item>
+      <uni-grid-item class="menu-grid" style="width: 134rpx; height: 134rpx">
+        <view class="menu-item" @click="navigateTo('/pages/enterprise/enterprise-list')">
+          <image class="item-image" src="@/static/images/index/ic_home_menu3.png"></image>
+          <view class="item-text"> 客户线索 </view>
+        </view>
+      </uni-grid-item>
+      <uni-grid-item class="menu-grid" style="width: 134rpx; height: 134rpx">
+        <view class="menu-item" @click="navigateTo('/pages/goods/goods-index')">
+          <image class="item-image" src="@/static/images/index/ic_home_menu4.png"></image>
+          <view class="item-text"> 品质商品 </view>
+        </view>
+      </uni-grid-item>
+      <uni-grid-item class="menu-grid" style="width: 134rpx; height: 134rpx">
+        <view class="menu-item" @click="navigateTo('/pages/support/change-log/change-log-list')">
+          <image class="item-image" src="@/static/images/index/ic_home_menu5.png"></image>
+          <view class="item-text"> 版本更新 </view>
+        </view>
+      </uni-grid-item>
+
+      <!--------------------------------- 第二排--------------------------------->
+      <uni-grid-item class="menu-grid" style="width: 134rpx; height: 134rpx">
+        <view class="menu-item" @click="navigateTo('/pages/form/form')">
+          <image class="item-image" src="@/static/images/index/ic_home_menu6.png"></image>
+          <view class="item-text"> 复杂表单 </view>
+        </view>
+      </uni-grid-item>
+      <uni-grid-item class="menu-grid" style="width: 134rpx; height: 134rpx">
+        <view class="menu-item" @click="navigateTo('/pages/order-detail/order-detail')">
+          <image class="item-image" src="@/static/images/index/ic_home_menu7.png"></image>
+          <view class="item-text"> 复杂详情 </view>
+        </view>
+      </uni-grid-item>
+      <uni-grid-item class="menu-grid" style="width: 134rpx; height: 134rpx">
+        <view class="menu-item" @click="switchTab('/pages/list/list')">
+          <image class="item-image" src="@/static/images/index/ic_home_menu9.png"></image>
+          <view class="item-text"> 列表样式1 </view>
+        </view>
+      </uni-grid-item>
+
+      <uni-grid-item class="menu-grid" style="width: 134rpx; height: 134rpx">
+        <view class="menu-item" @click="switchTab('/pages/list2/list')">
+          <image class="item-image" src="@/static/images/index/ic_home_menu8.png"></image>
+          <view class="item-text"> 列表样式2 </view>
+        </view>
+      </uni-grid-item>
+      <uni-grid-item class="menu-grid" style="width: 134rpx; height: 134rpx">
+        <view class="menu-item" @click="developing">
+          <image class="item-image" src="@/static/images/index/ic_home_menu10.png"></image>
+          <view class="item-text"> 接口加密 </view>
+        </view>
+      </uni-grid-item>
+    </uni-grid>
+  </view>
+</template>
+
+<script setup>
+  import { SmartToast } from '@/lib/smart-support';
+  const emit = defineEmits(['changeHome']);
+
+  function changeHome() {
+    emit('changeHome');
+  }
+
+  function navigateTo(url) {
+    uni.navigateTo({
+      url,
+    });
+  }
+  function switchTab(url) {
+    uni.switchTab({
+      url,
+    });
+  }
+
+  function developing() {
+    SmartToast.toast('敬请期待');
+  }
+</script>
+
+<style lang="scss" scoped>
+  .menu-container {
+    .menu-grid {
+      &:nth-child(n + 6) {
+        margin-top: 20rpx;
+      }
+    }
+    height: 340rpx;
+    box-sizing: border-box;
+    border-radius: 16rpx;
+    width: 700rpx;
+    margin: 0 auto 20rpx;
+    display: flex;
+    justify-content: space-between;
+    padding: 30rpx 10rpx;
+    flex-wrap: wrap;
+    background-color: #fff;
+    .menu-item {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      width: 134rpx;
+      height: 134rpx;
+      .item-image {
+        width: 80rpx;
+        height: 80rpx;
+        margin-bottom: 10rpx;
+      }
+      .item-text {
+        font-size: 26rpx;
+        color: #353535;
+      }
+    }
+  }
+</style>

+ 96 - 0
src/pages/home/components/notice.vue

@@ -0,0 +1,96 @@
+<template>
+  <view class="container">
+    <uni-card title="通知公告" :isFull="true" type="line" padding="0px" spacing="0px">
+      <template #extra> <view @click="onMore">查看更多</view> </template>
+      <uni-list>
+        <uni-list-item :ellipsis="1" v-for="item in data" :rightText="item.publishDate" clickable @click="goto(item.noticeId)" :title="item.title" />
+      </uni-list>
+    </uni-card>
+  </view>
+</template>
+
+<script setup>
+  import { ref } from 'vue';
+  import { noticeApi } from '@/api/business/oa/notice-api';
+  import { smartSentry } from '@/lib/smart-sentry';
+  import { onShow } from '@dcloudio/uni-app';
+
+  const queryForm = {
+    pageNum: 1,
+    pageSize: 5,
+    searchCount: false,
+  };
+
+  let data = ref([]);
+
+  const loading = ref(false);
+  // 查询列表
+  async function queryNoticeList() {
+    try {
+      loading.value = true;
+      const result = await noticeApi.queryEmployeeNotice(queryForm);
+      data.value = result.data.list;
+    } catch (err) {
+      smartSentry.captureError(err);
+    } finally {
+      loading.value = false;
+    }
+  }
+
+  // 跳转详情
+  function goto(noticeId) {
+    uni.navigateTo({ url: '/pages/notice/notice-detail?noticeId=' + noticeId });
+  }
+
+  onShow(() => {
+    queryNoticeList();
+  });
+
+  // 查看更多
+  function onMore() {
+    uni.navigateTo({
+      url: '/pages/notice/notice-index',
+    });
+  }
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    width: 700rpx;
+    margin: 0 auto 20rpx;
+    border-radius: 12rpx;
+    padding: 0;
+    overflow: hidden;
+
+    :deep(.uni-card__header-box) {
+      font-weight: bold;
+    }
+    :deep(.uni-list--border-bottom) {
+      background-color: transparent;
+    }
+    :deep(.uni-card__content) {
+      padding: 0 !important;
+    }
+    :deep(.uni-card__header) {
+      border: none;
+    }
+    :deep(.uni-card .uni-card__header .uni-card__header-content .uni-card__header-content-title) {
+      font-size: 32rpx;
+    }
+    :deep(.uni-list-item__container) {
+      padding: 16rpx 20rpx;
+    }
+    :deep(.uni-card__header) {
+      background: linear-gradient(180deg, #e8f4ff, #f8fcff);
+    }
+    :deep(.uni-card__header-extra) {
+      font-size: 30rpx;
+      font-weight: 400;
+      text-align: center;
+      color: #1a9aff;
+    }
+    :deep(.uni-list-item__content-title) {
+      font-size: 30rpx;
+    }
+  }
+</style>

+ 87 - 0
src/pages/home/components/statistics.vue

@@ -0,0 +1,87 @@
+<template>
+  <view class="statistics-view">
+    <view class="view-top">
+      <view class="view-top-item-left">
+        <view class="item-left-top"> 本月销售额(元) </view>
+        <view class="item-left-bottom"> ¥32,226.00 </view>
+      </view>
+      <view class="view-top-item-right">
+        <view class="item-right-top"> 本月销售目标 </view>
+        <view class="item-right-bottom"> ¥50,000.00 </view>
+      </view>
+    </view>
+    <view class="view-bottom">
+      <view class="view-bottom-item">
+        <view class="item-bottom-top"> 本月完成率 </view>
+        <view class="item-bottom-bottom"> 85% </view>
+      </view>
+      <view class="view-item">
+        <view class="item-bottom-top"> 本月新增客户 </view>
+        <view class="item-bottom-bottom">+1024 </view>
+      </view>
+      <view class="view-item">
+        <view class="item-bottom-top"> 本月累计通话 </view>
+        <view class="item-bottom-bottom"> 16:00小时 </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script></script>
+
+<style lang="scss" scoped>
+  .statistics-view {
+    width: 700rpx;
+    height: 300rpx;
+    margin: 10rpx auto 20rpx;
+    background: linear-gradient(133deg, #045ff0 0%, #2291f9 55%, #2168ec 100%);
+    border-radius: 6px;
+    .view-top {
+      display: flex;
+      padding: 36rpx 44rpx;
+      .view-top-item-left {
+        margin-right: 100rpx;
+        .item-left-top {
+          opacity: 0.6;
+          font-size: 26rpx;
+          color: #ffffff;
+          margin-bottom: 5rpx;
+        }
+        .item-left-bottom {
+          font-size: 52rpx;
+          font-weight: 700;
+          color: #ffffff;
+        }
+      }
+      .view-top-item-right {
+        .item-right-top {
+          opacity: 0.6;
+          font-size: 26rpx;
+          color: #ffffff;
+          margin-bottom: 20rpx;
+        }
+        .item-right-bottom {
+          font-size: 30rpx;
+          font-weight: 700;
+          color: #ffffff;
+        }
+      }
+    }
+    .view-bottom {
+      padding: 26rpx 44rpx;
+      display: flex;
+      justify-content: space-between;
+      .item-bottom-top {
+        opacity: 0.6;
+        font-size: 24rpx;
+        font-weight: 400;
+        color: #ffffff;
+      }
+      .item-bottom-bottom {
+        font-size: 30rpx;
+        font-weight: 500;
+        color: #ffffff;
+      }
+    }
+  }
+</style>

+ 80 - 0
src/pages/home/index.vue

@@ -0,0 +1,80 @@
+<template>
+  <view class="page">
+    <uni-nav-bar title="首页" :border="false" fixed>
+      <template #right>
+        <view class="right">
+          <view class="">
+            <image src="@/static/images/index/ic_scan.png" mode=""></image>
+          </view>
+          <view class="">
+            <image src="@/static/images/index/ic_search.png" mode=""></image>
+          </view>
+        </view>
+      </template>
+    </uni-nav-bar>
+
+    <!-- 数据表 -->
+    <Statistics v-if="!showBannerFlag" />
+
+    <!-- Banner -->
+    <Banner v-if="showBannerFlag" />
+
+    <!-- 功能菜单 -->
+    <Menu @changeHome="changeHome" />
+
+    <!-- 通知公告 -->
+    <Notice />
+
+    <!-- 商品 -->
+    <Goods />
+  </view>
+</template>
+
+<script setup>
+  import Banner from './components/banner.vue';
+  import Menu from './components/menu.vue';
+  import Statistics from './components/statistics.vue';
+  import Notice from './components/notice.vue';
+  import Goods from './components/goods.vue';
+  import { ref } from 'vue';
+  import { onShow } from '@dcloudio/uni-app';
+
+  const showBannerFlag = ref(false);
+  function changeHome() {
+    showBannerFlag.value = !showBannerFlag.value;
+  }
+
+  onShow(() => {
+    uni.pageScrollTo({
+      scrollTop: 0,
+      duration: 300,
+    });
+  });
+</script>
+
+<style lang="scss" scoped>
+  .page {
+    background-color: #f5f5f5;
+  }
+
+  :deep(.uni-navbar__header-btns) {
+    width: 150rpx !important;
+  }
+
+  .right {
+    position: relative;
+    display: flex;
+    z-index: 999;
+
+    view {
+      width: 56rpx;
+      height: 56rpx;
+      margin-left: 36rpx;
+
+      image {
+        width: 100%;
+        height: 100%;
+      }
+    }
+  }
+</style>

+ 154 - 0
src/pages/list/components/list-ui1.vue

@@ -0,0 +1,154 @@
+<template>
+  <view class="list-view">
+    <view class="item" v-for="item in data">
+      <view class="item-top">
+        <view class="item-top-info">
+          <view class="name"> {{ item.name }} </view>
+          <view class="mobile"> {{ item.phone }} </view>
+        </view>
+        <view class="item-top-phone"> <image src="/static/images/list/phone.png" mode=""></image> 联系Ta </view>
+      </view>
+      <view class="item-title"> 最后跟进内容:{{ item.content }} </view>
+      <view class="item-bottom"> 最后跟进时间:{{ item.time }} </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import { ref } from 'vue';
+  const data = ref([
+    {
+      name: '卓大',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+    {
+      name: '胡可',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+    {
+      name: '开云',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+    {
+      name: '清野',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+    {
+      name: '善逸',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+    {
+      name: '飞叶',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+    {
+      name: '启阳',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+    {
+      name: '卓大',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+    {
+      name: '卓大',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+    {
+      name: '卓大',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+    {
+      name: '卓大',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+    {
+      name: '卓大',
+      phone: '18610241024',
+      content: '上午跟客户谈了下合作意向,对方比较认可',
+      time: '2023-01-03 12:14:10',
+    },
+  ]);
+</script>
+
+<style lang="scss" scoped>
+  .list-view {
+    margin: 20rpx 24rpx;
+  }
+  .item {
+    box-sizing: border-box;
+    width: 700rpx;
+    background: #ffffff;
+    border-radius: 12rpx;
+    box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+    padding: 24rpx 30rpx;
+    border-bottom: 1rpx solid #ededed;
+    margin-bottom: 24rpx;
+
+    .item-top {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 20rpx;
+      .item-top-info {
+        display: flex;
+        align-items: center;
+        .name {
+          font-size: 34rpx;
+          font-weight: 500;
+          color: #444444;
+          margin-right: 10rpx;
+        }
+        .mobile {
+          font-size: 32rpx;
+          font-weight: bold;
+          text-align: left;
+          color: #444444;
+        }
+      }
+      .item-top-phone {
+        color: #1a9aff;
+        font-size: 28rpx;
+        display: flex;
+        align-items: center;
+        image {
+          width: 40rpx;
+          height: 40rpx;
+          margin-left: 4rpx;
+        }
+      }
+    }
+
+    .item-title {
+      font-size: 28rpx;
+      color: #777777;
+      margin-bottom: 24rpx;
+    }
+
+    .item-bottom {
+      font-size: 28rpx;
+      color: #777777;
+    }
+  }
+</style>

+ 143 - 0
src/pages/list/components/list-ui2.vue

@@ -0,0 +1,143 @@
+<template>
+  <view class="list-view">
+    <view class="item">
+      <view class="item-header">
+        <view class="item-top">
+          <view class="item-top-left">订单号:DD238647624875 </view>
+          <view class="item-top-right">
+            <view class="tag" type="warring"> 未付款 </view>
+          </view>
+        </view>
+        <view class="line-border-bottom"> </view>
+        <view class="item-title"> 小米手机20S </view>
+        <view class="item-sub-title"> 订单金额:¥5852.00 </view>
+        <view class="item-sub-title"> 客户:1024创新实验室 </view>
+        <view class="item-sub-title"> 下单时间: 2023-08-20 19:15 </view>
+      </view>
+
+      <view class="order-end-time"> 剩余 29:35:07 关闭订单 </view>
+    </view>
+
+    <view class="item">
+      <view class="item-header">
+        <view class="item-top">
+          <view class="item-top-left">订单号:DD2382354875 </view>
+          <view class="item-top-right">
+            <view class="tag" type="success"> 完成 </view>
+          </view>
+        </view>
+        <view class="line-border-bottom"> </view>
+        <view class="item-title"> 苹果Iphone30 </view>
+        <view class="item-sub-title"> 订单金额:¥5852.00 </view>
+        <view class="item-sub-title"> 客户:卓大 </view>
+        <view class="item-sub-title"> 下单时间: 2023-08-20 19:15 </view>
+      </view>
+    </view>
+
+    <view class="item">
+      <view class="item-header">
+        <view class="item-top">
+          <view class="item-top-left">订单号:DD238643287 </view>
+          <view class="item-top-right">
+            <view class="tag" type="success"> 完成 </view>
+          </view>
+        </view>
+        <view class="line-border-bottom"> </view>
+        <view class="item-title"> 华为Mate100 </view>
+        <view class="item-sub-title"> 订单金额:¥5852.00 </view>
+        <view class="item-sub-title"> 客户:卓大 </view>
+        <view class="item-sub-title"> 下单时间: 2023-08-20 19:15 </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  defineProps({
+    list: {
+      type: Array,
+      default: [],
+    },
+  });
+</script>
+
+<style lang="scss" scoped>
+  .list-view {
+    margin: 20rpx 24rpx;
+  }
+  .item {
+    .item-header {
+      padding: 0 30rpx;
+    }
+    box-sizing: border-box;
+    width: 700rpx;
+    background: #ffffff;
+    border-radius: 12rpx;
+    box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+    padding-top: 24rpx;
+    margin-bottom: 30rpx;
+    overflow: hidden;
+
+    .item-top {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 20rpx;
+      .item-top-left {
+        font-size: 28rpx;
+        font-weight: 400;
+        color: #777777;
+      }
+      .item-top-right {
+        .tag {
+          width: 112rpx;
+          height: 40rpx;
+          border-radius: 4rpx;
+          text-align: center;
+          line-height: 40rpx;
+          font-size: 24rpx;
+          &[type='warring'] {
+            color: #ff6c00;
+            background: #fff0ed;
+          }
+          &[type='success'] {
+            color: $uni-color-success;
+            background: #f7f8f9;
+          }
+        }
+      }
+    }
+
+    .item-title {
+      margin-top: 20rpx;
+      margin-bottom: 12rpx;
+      font-size: 34rpx;
+      font-weight: 700;
+      color: #444444;
+    }
+
+    .item-sub-title {
+      font-size: 28rpx;
+      color: #777777;
+      margin-top: 20rpx;
+      &:last-child {
+        padding-bottom: 20rpx;
+        margin-bottom: 0;
+      }
+    }
+
+    .order-end-time {
+      height: 40rpx;
+      background: #ff6c00;
+      box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+      color: #fff;
+      text-align: center;
+      font-size: 24rpx;
+      line-height: 40rpx;
+    }
+  }
+
+  .line-border-bottom {
+    border-bottom: 1rpx solid #ededed;
+  }
+</style>

+ 335 - 0
src/pages/list/components/list-ui3.vue

@@ -0,0 +1,335 @@
+<template>
+  <view class="list-view">
+    <view class="item">
+      <view class="item-header">
+        <view class="item-top">
+          <view class="item-top-left"> 2023-08-20 19:15 </view>
+          <view class="item-top-right">
+            <view class="tag" type="warring"> 未付款 </view>
+          </view>
+        </view>
+        <view class="line-border-bottom"> </view>
+        <view class="item-center">
+          <view class="item-center-left">
+            <view class="item-top-z"> 装 </view>
+            <view class="item-center"> </view>
+            <view class="item-bottom-x"> 卸 </view>
+          </view>
+          <view class="item-center-address">
+            <view class="m-b-16">
+              <view class="item-title"> 装货地址 </view>
+              <view class="item-address"> 河南省洛阳市洛龙区110号 </view>
+            </view>
+            <view class="m-b-16">
+              <view class="item-title"> 卸货地址 </view>
+              <view class="item-address"> 河南省洛阳市西工区120号 </view>
+            </view>
+          </view>
+        </view>
+
+        <view class="line-border-bottom"> </view>
+        <view class="item-footer">
+          <view class="item-footer-left">
+            <text>¥586.00</text>
+          </view>
+          <view class="item-footer-right">
+            <view class="camera">
+              <image src="/static/images/list/camera.png" mode=""></image>
+              拍照
+            </view>
+            <view class="ok">
+              <image src="/static/images/list/success.png" mode=""></image>
+              完成
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <view class="item">
+      <view class="item-header">
+        <view class="item-top">
+          <view class="item-top-left"> 2023-08-20 19:15 </view>
+          <view class="item-top-right">
+            <view class="tag" type="warring"> 未付款 </view>
+          </view>
+        </view>
+        <view class="line-border-bottom"> </view>
+        <view class="item-center">
+          <view class="item-center-left">
+            <view class="item-top-z"> 装 </view>
+            <view class="item-center"> </view>
+            <view class="item-bottom-x"> 卸 </view>
+          </view>
+          <view class="item-center-address">
+            <view class="m-b-16">
+              <view class="item-title"> 装货地址 </view>
+              <view class="item-address"> 河南省洛阳市洛龙区110号 </view>
+            </view>
+            <view class="m-b-16">
+              <view class="item-title"> 卸货地址 </view>
+              <view class="item-address"> 河南省洛阳市西工区120号 </view>
+            </view>
+          </view>
+        </view>
+
+        <view class="line-border-bottom"> </view>
+        <view class="item-footer">
+          <view class="item-footer-left">
+            <text>¥586.00</text>
+          </view>
+          <view class="item-footer-right">
+            <view class="camera">
+              <image src="/static/images/list/camera.png" mode=""></image>
+              拍照
+            </view>
+            <view class="ok">
+              <image src="/static/images/list/success.png" mode=""></image>
+              完成
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <view class="item">
+      <view class="item-header">
+        <view class="item-top">
+          <view class="item-top-left"> 2023-08-20 19:15 </view>
+          <view class="item-top-right">
+            <view class="tag" type="warring"> 未付款 </view>
+          </view>
+        </view>
+        <view class="line-border-bottom"> </view>
+        <view class="item-center">
+          <view class="item-center-left">
+            <view class="item-top-z"> 装 </view>
+            <view class="item-center"> </view>
+            <view class="item-bottom-x"> 卸 </view>
+          </view>
+          <view class="item-center-address">
+            <view class="m-b-16">
+              <view class="item-title"> 装货地址 </view>
+              <view class="item-address"> 河南省洛阳市洛龙区110号 </view>
+            </view>
+            <view class="m-b-16">
+              <view class="item-title"> 卸货地址 </view>
+              <view class="item-address"> 河南省洛阳市西工区120号 </view>
+            </view>
+          </view>
+        </view>
+
+        <view class="line-border-bottom"> </view>
+        <view class="item-footer">
+          <view class="item-footer-left">
+            <text>¥586.00</text>
+          </view>
+          <view class="item-footer-right">
+            <view class="camera">
+              <image src="/static/images/list/camera.png" mode=""></image>
+              拍照
+            </view>
+            <view class="ok">
+              <image src="/static/images/list/success.png" mode=""></image>
+              完成
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  defineProps({
+    list: {
+      type: Array,
+      default: [],
+    },
+  });
+</script>
+
+<style lang="scss" scoped>
+  .list-view {
+    margin: 20rpx 24rpx;
+  }
+
+  .item {
+    .item-header {
+      padding: 0 30rpx;
+    }
+
+    box-sizing: border-box;
+    width: 700rpx;
+    background: #ffffff;
+    border-radius: 12rpx;
+    box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+    padding-top: 24rpx;
+    margin-bottom: 30rpx;
+    overflow: hidden;
+
+    .item-top {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 20rpx;
+
+      .item-top-left {
+        font-size: 28rpx;
+        font-weight: 400;
+        color: #777777;
+      }
+
+      .item-top-right {
+        .tag {
+          width: 112rpx;
+          height: 40rpx;
+          border-radius: 4rpx;
+          text-align: center;
+          line-height: 40rpx;
+          font-size: 24rpx;
+
+          &[type='warring'] {
+            color: #ff6c00;
+            background: #fff0ed;
+          }
+          &[type='success'] {
+            color: #1a9aff;
+            background: #f2f9ff;
+          }
+        }
+      }
+    }
+
+    .item-center {
+      margin-top: 20rpx;
+      display: flex;
+
+      .item-center-left {
+        margin-top: 5rpx;
+        margin-right: 12rpx;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+
+        .item-top-z {
+          position: relative;
+          z-index: 10;
+          width: 34rpx;
+          height: 34rpx;
+          background: #1a9aff;
+          border-radius: 12rpx;
+          text-align: center;
+          font-size: 24rpx;
+          color: #fff;
+        }
+
+        .item-center {
+          height: 75rpx;
+          border-left: 1rpx dashed #777777;
+          margin-top: -5rpx;
+          border-spacing: 6rpx;
+          border-width: 2rpx;
+        }
+
+        .item-bottom-x {
+          width: 34rpx;
+          height: 34rpx;
+          background: #ff6c00;
+          border-radius: 12rpx;
+          text-align: center;
+          font-size: 24rpx;
+          color: #fff;
+        }
+      }
+
+      .item-center-address {
+        .m-b-16 {
+          margin-bottom: 16rpx;
+        }
+        .item-title {
+          font-size: 34rpx;
+          color: #444444;
+          font-weight: bold;
+          margin-bottom: 4rpx;
+        }
+
+        .item-address {
+          font-size: 28rpx;
+          color: #777777;
+        }
+      }
+    }
+
+    .item-sub-title {
+      font-size: 28rpx;
+      color: #777777;
+      margin-bottom: 10rpx;
+
+      &:last-child {
+        padding-bottom: 20rpx;
+        margin-bottom: 0;
+      }
+    }
+
+    .order-end-time {
+      height: 40rpx;
+      background: #ff6c00;
+      box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+      color: #fff;
+      text-align: center;
+      font-size: 24rpx;
+      line-height: 40rpx;
+    }
+  }
+
+  .item-footer {
+    padding: 20rpx 0;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+
+    .item-footer-left {
+      font-size: 34rpx;
+      color: #777777;
+
+      text {
+        color: #ff3924;
+        font-weight: bold;
+      }
+    }
+
+    .item-footer-right {
+      display: flex;
+      align-items: center;
+
+      view {
+        width: 160rpx;
+        height: 64rpx;
+        border-radius: 8rpx;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        color: #fff;
+
+        image {
+          width: 40rpx;
+          height: 40rpx;
+        }
+
+        &.ok {
+          background: #1a9aff;
+          margin-left: 24rpx;
+        }
+
+        &.camera {
+          background: linear-gradient(107deg, #57d697 9%, #40c87e 86%);
+        }
+      }
+    }
+  }
+
+  .line-border-bottom {
+    border-bottom: 1rpx solid #ededed;
+  }
+</style>

+ 246 - 0
src/pages/list/components/list-ui4.vue

@@ -0,0 +1,246 @@
+<template>
+  <view class="list-view">
+    <view class="item">
+      <view class="item-top">
+        <view class="item-top-item">
+          <view class="item-top-label"> 创建时间: </view>
+          <view class="item-title"> 2023-08-27 21:33 </view>
+          <view class="item-state">
+            <view class="tag" type="warring"> 未付款 </view>
+          </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 客户名称: </view>
+          <view class="item-title"> 六边形工程师 </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 供应商名称: </view>
+          <view class="item-title"> 1024数字科技有限公司 </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 客户经理: </view>
+          <view class="item-title"> 卓大 </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 生产厂商: </view>
+          <view class="item-title"> 1024工厂 </view>
+        </view>
+      </view>
+
+      <view class="item-footer">
+        <view class="item-footer-left"> 付款金额: </view>
+        <view class="item-footer-right"> ¥300000.00 </view>
+      </view>
+    </view>
+
+    <view class="item">
+      <view class="item-top">
+        <view class="item-top-item">
+          <view class="item-top-label"> 创建时间: </view>
+          <view class="item-title"> 2023-08-27 21:33 </view>
+          <view class="item-state">
+            <view class="tag" type="success"> 已付款 </view>
+          </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 客户名称: </view>
+          <view class="item-title"> 六边形工程师 </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 供应商名称: </view>
+          <view class="item-title"> 1024数字科技有限公司 </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 客户经理: </view>
+          <view class="item-title"> 卓大 </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 生产厂商: </view>
+          <view class="item-title"> 1024工厂 </view>
+        </view>
+      </view>
+
+      <view class="item-footer">
+        <view class="item-footer-left"> 付款金额: </view>
+        <view class="item-footer-right"> ¥300000.00 </view>
+      </view>
+    </view>
+
+    <view class="item">
+      <view class="item-top">
+        <view class="item-top-item">
+          <view class="item-top-label"> 创建时间: </view>
+          <view class="item-title"> 2024-01-11 21:33 </view>
+          <view class="item-state">
+            <view class="tag" type="warring"> 未付款 </view>
+          </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 客户名称: </view>
+          <view class="item-title"> 孙传芳 </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 供应商名称: </view>
+          <view class="item-title"> 1024数字科技有限公司 </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 客户经理: </view>
+          <view class="item-title"> 胡克 </view>
+        </view>
+        <view class="item-top-item">
+          <view class="item-top-label"> 生产厂商: </view>
+          <view class="item-title"> 数字工厂 </view>
+        </view>
+      </view>
+
+      <view class="item-footer">
+        <view class="item-footer-left"> 付款金额: </view>
+        <view class="item-footer-right"> ¥300000.00 </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  defineProps({
+    list: {
+      type: Array,
+      default: [],
+    },
+  });
+</script>
+
+<style lang="scss" scoped>
+  .decollator {
+    height: 30rpx;
+    position: relative;
+    display: flex;
+    align-items: center;
+    .decollator-left {
+      top: 2rpx;
+      position: absolute;
+      width: 60rpx;
+      height: 60rpx;
+      border-radius: 50%;
+      background: #f5f5f5;
+      z-index: 10;
+      left: -30rpx;
+    }
+    .decollator-right {
+      top: 2rpx;
+      width: 60rpx;
+      height: 60rpx;
+      border-radius: 50%;
+      background: #f5f5f5;
+      position: absolute;
+      z-index: 10;
+      right: -30rpx;
+    }
+    .decollator-center {
+      width: 100%;
+      position: absolute;
+      bottom: 0;
+      z-index: 1;
+      border-top: 1rpx dashed #ededed;
+    }
+  }
+
+  .list-view {
+    margin: 20rpx 24rpx;
+  }
+  .item {
+    box-sizing: border-box;
+    width: 700rpx;
+    background: #ffffff;
+    border-radius: 12rpx;
+    box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+    padding-top: 24rpx;
+    margin-bottom: 30rpx;
+    overflow: hidden;
+
+    .item-top {
+      padding: 0 30rpx;
+      .item-top-item {
+        display: flex;
+        align-items: center;
+        margin-bottom: 16rpx;
+      }
+      margin-bottom: 20rpx;
+      .item-top-label {
+        width: 168rpx;
+        margin-right: 10rpx;
+        font-size: 28rpx;
+        color: #777777;
+      }
+      .item-title {
+        font-size: 28rpx;
+        color: #323333;
+        font-weight: bold;
+      }
+      .item-state {
+        flex: 1;
+        display: flex;
+        justify-content: flex-end;
+        .tag {
+          width: 112rpx;
+          height: 40rpx;
+          border-radius: 4rpx;
+          text-align: center;
+          line-height: 40rpx;
+          font-size: 24rpx;
+          &[type='warring'] {
+            background-color: $error-color;
+            color: white;
+          }
+          &[type='success'] {
+            background-color: $uni-color-success;
+            color: white;
+          }
+        }
+      }
+    }
+
+    .item-sub-title {
+      font-size: 28rpx;
+      color: #777777;
+      margin-bottom: 10rpx;
+      &:last-child {
+        padding-bottom: 20rpx;
+        margin-bottom: 0;
+      }
+    }
+
+    .order-end-time {
+      height: 40rpx;
+      background: #ff6c00;
+      box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+      color: #fff;
+      text-align: center;
+      font-size: 24rpx;
+      line-height: 40rpx;
+    }
+  }
+
+  .line-border-bottom {
+    border-bottom: 1rpx solid #ededed;
+  }
+
+  .item-footer {
+    height: 120rpx;
+    display: flex;
+    border-top: 1rpx dashed #ccc;
+    //background-color: #F7FAFD;
+    align-items: center;
+    justify-content: space-between;
+    margin: 0 30rpx;
+    .item-footer-left {
+      font-size: 28rpx;
+      color: #777;
+    }
+    .item-footer-right {
+      font-size: 34rpx;
+      color: red;
+      font-weight: bold;
+    }
+  }
+</style>

+ 47 - 0
src/pages/list/list.vue

@@ -0,0 +1,47 @@
+<template>
+  <view>
+    <y-tabs v-model="active" sticky :offsetTop="43" color="#007aff">
+      <y-tab v-for="item in tabsList" :key="item.value" :title="item.title"> </y-tab>
+    </y-tabs>
+
+    <ListUI1 v-if="active === 0" />
+    <ListUI2 v-if="active === 1" />
+    <ListUI3 v-if="active === 2" />
+    <ListUI4 v-if="active === 3" />
+  </view>
+</template>
+
+<script setup>
+  import { ref } from 'vue';
+  import ListUI1 from './components/list-ui1.vue';
+  import ListUI2 from './components/list-ui2.vue';
+  import ListUI3 from './components/list-ui3.vue';
+  import ListUI4 from './components/list-ui4.vue';
+
+  const active = ref(0);
+
+  const tabsList = [
+    {
+      title: '线索列表',
+      value: 0,
+    },
+    {
+      title: '我的订单',
+      value: 1,
+    },
+    {
+      title: '我的运单',
+      value: 2,
+    },
+    {
+      title: '付款单',
+      value: 3,
+    },
+  ];
+</script>
+
+<style lang="scss" scoped>
+  page {
+    background-color: #f5f5f5;
+  }
+</style>

+ 108 - 0
src/pages/list2/components/course-list.vue

@@ -0,0 +1,108 @@
+<template>
+  <view class="item" v-for="item in 10">
+    <view class="item-header">
+      <view class="header-title"> 商业环境快速变化带来的挑战 </view>
+      <view class="header-right"> 共15讲> </view>
+    </view>
+    <view class="item-tags">
+      <view class="primary"> 房地产 </view>
+      <view class="success"> 国债 </view>
+      <view class="warning"> 贸易 </view>
+    </view>
+    <view class="content">
+      <view class="content-box">
+        <view class="c-top"> 授课老师 </view>
+        <view class="c-bottom"> 秦始皇 </view>
+      </view>
+      <view class="content-box">
+        <view class="c-top"> 开课时间 </view>
+        <view class="c-bottom"> 2023.08.20 20:00 </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  defineProps({
+    list: {
+      type: Array,
+      default: [],
+    },
+  });
+</script>
+
+<style lang="scss" scoped>
+  .item {
+    width: 700rpx;
+    margin: 20rpx auto 0;
+    background: #ffffff;
+    border-radius: 12rpx;
+    box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+    padding: 26rpx 30rpx 20rpx;
+    box-sizing: border-box;
+    .item-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: flex-start;
+      .header-title {
+        width: 480rpx;
+        font-size: 34rpx;
+        color: #444;
+        font-weight: 600;
+      }
+      .header-right {
+        margin-top: 4rpx;
+        font-size: 28rpx;
+        color: #777777;
+      }
+    }
+    .item-tags {
+      display: flex;
+      margin: 10rpx 0;
+      view {
+        width: 112rpx;
+        height: 42rpx;
+        border-radius: 4rpx;
+        text-align: center;
+        line-height: 42rpx;
+        font-size: 24rpx;
+        margin-right: 24rpx;
+        &.primary {
+          background: #eef7fd;
+          color: #1a9aff;
+        }
+        &.success {
+          color: #1ce36d;
+          background: #e5f8e9;
+        }
+        &.warning {
+          background: #fff0ed;
+          color: #ff6c00;
+        }
+      }
+    }
+    .content {
+      width: 100%;
+      height: 140rpx;
+      background: #f7f8f9;
+      border-radius: 4px;
+      display: flex;
+      justify-content: space-around;
+      align-items: center;
+      .content-box {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+      }
+      .c-top {
+        font-size: 24rpx;
+        color: #777777;
+        margin-bottom: 4rpx;
+      }
+      .c-bottom {
+        font-size: 30rpx;
+        color: #444444;
+      }
+    }
+  }
+</style>

+ 149 - 0
src/pages/list2/components/discount-list.vue

@@ -0,0 +1,149 @@
+<template>
+  <view class="item">
+    <view class="header">
+      <view class="header-left">
+        <view class="header-left-title"> 手机优惠券 </view>
+        <view class="header-left-time"> 有效期至2024.08.28 </view>
+      </view>
+      <view class="header-right-price"> ¥<text>10</text> </view>
+    </view>
+    <view class="footer">
+      <view class="footer-left"> 适用于:双11商品 </view>
+      <view v-if="index != 1" class="footer-right"> 去使用 </view>
+    </view>
+  </view>
+  <view class="item">
+    <view class="forbidden">
+      <image src="/static/images/pure-list/employ.png" mode=""></image>
+    </view>
+    <view class="header">
+      <view class="header-left">
+        <view class="header-left-title"> 笔记本优惠券 </view>
+        <view class="header-left-time"> 有效期至2024.08.28 </view>
+      </view>
+      <view class="header-right-price grayscale"> ¥<text>99</text> </view>
+    </view>
+    <view class="footer">
+      <view class="footer-left"> 适用于:双11商品 </view>
+    </view>
+  </view>
+  <view class="item">
+    <view class="header">
+      <view class="header-left">
+        <view class="header-left-title"> 汽车优惠券 </view>
+        <view class="header-left-time"> 有效期至2024.08.28 </view>
+      </view>
+      <view class="header-right-price"> ¥<text>9999</text> </view>
+    </view>
+    <view class="footer">
+      <view class="footer-left"> 适用于:双11商品 </view>
+      <view v-if="index != 1" class="footer-right"> 去使用 </view>
+    </view>
+  </view>
+  <view class="item">
+    <view class="forbidden">
+      <image src="/static/images/pure-list/employ.png" mode=""></image>
+    </view>
+    <view class="header">
+      <view class="header-left">
+        <view class="header-left-title"> 笔记本优惠券 </view>
+        <view class="header-left-time"> 有效期至2024.08.28 </view>
+      </view>
+      <view class="header-right-price grayscale"> ¥<text>99</text> </view>
+    </view>
+    <view class="footer">
+      <view class="footer-left"> 适用于:双11商品 </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  defineProps({
+    list: {
+      type: Array,
+      default: [],
+    },
+  });
+</script>
+
+<style lang="scss" scoped>
+  .forbidden {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    background: rgba(255, 255, 255, 0.5);
+    left: 0;
+    top: 0;
+    image {
+      position: absolute;
+      right: 0;
+      bottom: 0;
+      width: 184rpx;
+      height: 160rpx;
+    }
+  }
+  .item {
+    position: relative;
+    width: 700rpx;
+    margin: 20rpx auto 0;
+    background: #ffffff;
+    border-radius: 12rpx;
+    box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+    padding: 26rpx 40rpx 20rpx;
+    box-sizing: border-box;
+    .header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding-bottom: 20rpx;
+      border-bottom: 1rpx dashed #ededed;
+      margin-bottom: 16rpx;
+      .header-left {
+        .header-left-title {
+          font-size: 34rpx;
+          font-weight: 500;
+          text-align: left;
+          color: #444444;
+          margin-bottom: 8rpx;
+        }
+        .header-left-time {
+          font-size: 28rpx;
+          color: #777;
+        }
+      }
+      .header-right-price {
+        color: #ff3924;
+        font-size: 42rpx;
+        font-weight: bold;
+        text {
+          font-size: 72rpx;
+        }
+      }
+      .grayscale {
+        color: #ccc;
+      }
+    }
+
+    .footer {
+      height: 64rpx;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      .footer-left {
+        font-size: 28rpx;
+        color: #777777;
+      }
+      .footer-right {
+        width: 144rpx;
+        height: 64rpx;
+        background: linear-gradient(112deg, #f7681d 9%, #f64226 84%);
+        border-radius: 8rpx;
+        box-shadow: 0px 5px 8px 0px rgba(236, 79, 40, 0.2);
+        color: #fff;
+        font-size: 28rpx;
+        text-align: center;
+        line-height: 64rpx;
+      }
+    }
+  }
+</style>

+ 159 - 0
src/pages/list2/components/express-list.vue

@@ -0,0 +1,159 @@
+<template>
+  <view class="item" v-for="(item, index) in 6">
+    <view class="item-header">
+      <view class="header-title"> 快递号:KD12382387 </view>
+      <view class="header-right" :class="index == 1 ? 'pay' : 'nopay'">
+        {{ index == 1 ? '已支付' : '未支付' }}
+      </view>
+    </view>
+    <view class="address">
+      <view class="start-address">
+        <view class="atom start">
+          <view> </view>
+        </view>
+        <view class="start-address-right">
+          <view class="start-address-text"> 出发地:洛阳市六边形科技园 </view>
+          <view class="start-time"> 出发时间:2023.08.23 </view>
+        </view>
+      </view>
+      <view class="end-address">
+        <view class="atom end">
+          <view> </view>
+        </view>
+        <view class="start-address-text"> 目的地:上海市壹零贰肆软件园 </view>
+      </view>
+    </view>
+    <view class="footer">
+      <view class="footer-order-time"> 下单时间:2023.08.23 </view>
+      <view class="footer-order-price"> 订单金额<text class="pay" :class="index == 1 ? 'pay' : 'nopay'">¥450.00</text> </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  defineProps({
+    list: {
+      type: Array,
+      default: [],
+    },
+  });
+</script>
+
+<style lang="scss" scoped>
+  .item {
+    width: 700rpx;
+    margin: 30rpx auto 0;
+    background: #ffffff;
+    border-radius: 12rpx;
+    box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+    padding: 26rpx 30rpx 20rpx;
+    box-sizing: border-box;
+    .item-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: flex-start;
+      position: relative;
+      margin-bottom: 26rpx;
+      .header-title {
+        width: 480rpx;
+        font-size: 28rpx;
+        font-weight: bold;
+        color: #444;
+      }
+      .header-right {
+        position: absolute;
+        right: -40rpx;
+        background-size: 138rpx 60rpx;
+        width: 138rpx;
+        height: 60rpx;
+        font-size: 28rpx;
+        color: #fff;
+        line-height: 50rpx;
+        text-indent: 24rpx;
+        &.pay {
+          background-image: url('/static/images/pure-list/blue.png');
+        }
+        &.nopay {
+          background-image: url('/static/images/pure-list/orange.png');
+        }
+      }
+    }
+    .address {
+      width: 100%;
+      background: #f7f8f9;
+      border-radius: 4px;
+      padding: 20rpx 24rpx;
+      box-sizing: border-box;
+      margin-bottom: 30rpx;
+      .atom {
+        width: 30rpx;
+        height: 30rpx;
+        border-radius: 50%;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        margin-right: 10rpx;
+        margin-top: 6rpx;
+        view {
+          width: 14rpx;
+          height: 14rpx;
+          border-radius: 50%;
+        }
+        &.start {
+          background: rgba(26, 154, 255, 0.2);
+          view {
+            background: rgb(26, 154, 255);
+          }
+        }
+        &.end {
+          background: rgba(255, 108, 0, 0.2);
+          view {
+            background: rgb(255, 108, 0);
+          }
+        }
+      }
+      .end-address {
+        display: flex;
+        align-items: flex-start;
+      }
+      .start-address-text {
+        font-size: 30rpx;
+        font-weight: bold;
+        color: #444444;
+        margin-bottom: 4rpx;
+      }
+      .start-address {
+        display: flex;
+        align-items: flex-start;
+
+        .start-time {
+          font-size: 28rpx;
+          color: #777777;
+        }
+        margin-bottom: 20rpx;
+      }
+    }
+    .footer {
+      display: flex;
+      justify-content: space-between;
+      .footer-order-time {
+        font-size: 28rpx;
+        color: #777;
+      }
+      .footer-order-price {
+        color: #777;
+        font-size: 28rpx;
+        text {
+          font-weight: bold;
+          margin-left: 4rpx;
+          &.pay {
+            color: #323333;
+          }
+          &.nopay {
+            color: #ff3924;
+          }
+        }
+      }
+    }
+  }
+</style>

+ 94 - 0
src/pages/list2/components/iot-list.vue

@@ -0,0 +1,94 @@
+<template>
+  <view class="item" v-for="(item, index) in 12" :key="index" :class="type[index]">
+    <view class="header"> PLC控制柜 </view>
+    <view class="footer">
+      <view class="footer-left"> 气路总阀:{{ date }} </view>
+      <view class="footer-state" :class="type[index]"> 关闭 </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import dayjs from 'dayjs';
+
+  defineProps({
+    list: {
+      type: Array,
+      default: [],
+    },
+  });
+
+  const date = dayjs().format('YYYY-MM-DD HH:mm:ss');
+
+  const type = [
+    'info',
+    'primary',
+    'warning',
+    '',
+    'info',
+    'primary',
+    'warning',
+    '',
+    'info',
+    'primary',
+    'warning',
+    '',
+    'info',
+    'primary',
+    'warning',
+    '',
+  ];
+</script>
+
+<style lang="scss" scoped>
+  .item {
+    width: 700rpx;
+    margin: 20rpx auto 0;
+    background: #ffffff;
+    border-radius: 12rpx;
+    box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+    padding: 26rpx 40rpx 20rpx;
+    box-sizing: border-box;
+    border-left: 8rpx solid #4f4d4d;
+    &.primary {
+      border-color: #007ff2;
+    }
+    &.warning {
+      border-color: #ff6c00;
+    }
+    &.info {
+      border-color: #cccccc;
+    }
+
+    .header {
+      display: flex;
+      justify-content: space-between;
+      padding-bottom: 20rpx;
+      border-bottom: 1rpx solid #ededed;
+      margin-bottom: 19rpx;
+      font-size: 34rpx;
+      color: #444;
+    }
+
+    .footer {
+      display: flex;
+      justify-content: space-between;
+      font-size: 28rpx;
+      .footer-left {
+        color: #777777;
+      }
+      .footer-state {
+        &.primary {
+          color: #007ff2;
+        }
+        &.warning {
+          color: #ff6c00;
+        }
+        &.info {
+          color: #cccccc;
+        }
+        color: #4f4d4d;
+      }
+    }
+  }
+</style>

+ 100 - 0
src/pages/list2/components/service-list.vue

@@ -0,0 +1,100 @@
+<template>
+  <view class="item" v-for="(item, index) in 6" :key="index">
+    <view class="header">
+      <view class="header-left">
+        <image class="header-left-image" src="/static/images/pure-list/maintain.png" mode=""></image>
+        <view class="header-left-title"> 反馈信息 </view>
+      </view>
+      <view class="header-right-id"> 保修单还:12876 </view>
+    </view>
+    <view class="footer">
+      <view class="footer-item">
+        <view class="label"> 商品名称:</view>
+        <view class="info"> 家用净水器 </view>
+      </view>
+      <view class="footer-item">
+        <view class="label"> 描述情况:</view>
+        <view class="info"> 水有杂志且存在异味 </view>
+      </view>
+      <view class="footer-item">
+        <view class="label">提交人:</view>
+        <view class="info"> 刘邦</view>
+      </view>
+      <view class="footer-item">
+        <view class="label"> 受理人员: </view>
+        <view class="info"> 迪丽热巴 </view>
+      </view>
+      <view class="footer-item">
+        <view class="label"> 提交时间: </view>
+        <view class="info"> 2023-01-01 12:12:12 </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  defineProps({
+    list: {
+      type: Array,
+      default: [],
+    },
+  });
+</script>
+
+<style lang="scss" scoped>
+  .item {
+    position: relative;
+    width: 700rpx;
+    margin: 20rpx auto 0;
+    background: #ffffff;
+    border-radius: 12rpx;
+    box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+    padding: 26rpx 40rpx 20rpx;
+    box-sizing: border-box;
+    .header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding-bottom: 20rpx;
+      border-bottom: 1rpx solid #ededed;
+      margin-bottom: 16rpx;
+
+      .header-left {
+        display: flex;
+        align-items: center;
+        .header-left-image {
+          width: 48rpx;
+          height: 48rpx;
+          margin-right: 16rpx;
+        }
+        .header-left-title {
+          font-size: 34rpx;
+          font-weight: bold;
+          color: #444444;
+        }
+      }
+      .header-right-id {
+        font-size: 28rpx;
+        color: #777777;
+      }
+    }
+
+    .footer {
+      .footer-item {
+        display: flex;
+        align-items: center;
+        margin-bottom: 16rpx;
+        .label {
+          width: 80px;
+          font-size: 28rpx;
+          color: #777777;
+        }
+        .info {
+          font-size: 28rpx;
+          color: #323333;
+          font-weight: bold;
+        }
+      }
+    }
+  }
+</style>

+ 55 - 0
src/pages/list2/list.vue

@@ -0,0 +1,55 @@
+<template>
+  <view>
+    <y-tabs v-model="active" sticky :offsetTop="43" color="#007aff">
+      <y-tab v-for="item in tabsList" :key="item.value" :title="item.title"> </y-tab>
+    </y-tabs>
+
+    <ExpressList v-if="active === 0" />
+    <DiscountList v-if="active === 1" />
+    <IotList v-if="active === 2" />
+    <ServiceList v-if="active === 3" />
+    <CourseList v-if="active === 4" />
+  </view>
+</template>
+
+<script setup>
+  import CourseList from './components/course-list.vue';
+  import ExpressList from './components/express-list.vue';
+  import DiscountList from './components/discount-list.vue';
+  import ServiceList from './components/service-list.vue';
+  import IotList from './components/iot-list.vue';
+
+  import { ref } from 'vue';
+
+  const active = ref(0);
+
+  const tabsList = [
+    {
+      title: '快递',
+      value: 0,
+    },
+    {
+      title: '优惠卷',
+      value: 1,
+    },
+    {
+      title: '物联网',
+      value: 2,
+    },
+    {
+      title: '售后',
+      value: 3,
+    },
+
+    {
+      title: '课程',
+      value: 4,
+    },
+  ];
+</script>
+
+<style lang="scss" scoped>
+  page {
+    background-color: #f5f5f5;
+  }
+</style>

+ 57 - 0
src/pages/login/components/login-check-box.vue

@@ -0,0 +1,57 @@
+<template>
+  <view class="check-box">
+    <view class="check-item">
+      <image @click="agree" :src="!agreeFlag ? checkOutImg : checkInImg" />
+      <span>
+        我已阅读并同意
+        <span class="link" @click="openProtocol('user_agreement')">《用户协议》</span>
+        与
+        <span class="link" @click="openProtocol('privacy_terms')">《隐私政策》</span>
+      </span>
+    </view>
+  </view>
+</template>
+<script setup>
+  import checkOutImg from '/static/images/login/check-out.png';
+  import checkInImg from '/static/images/login/check-in.png';
+
+  import { ref } from 'vue';
+
+  const agreeFlag = ref(true);
+
+  function agree() {
+    agreeFlag.value = !agreeFlag.value;
+  }
+
+  function openProtocol(protocolKey) {
+    uni.navigateTo({
+      url: `/pages/protocol/index?key=${protocolKey}`,
+    });
+  }
+
+  defineExpose({
+    agreeFlag,
+  });
+</script>
+<style lang="scss" scoped>
+  .check-box {
+    .check-item {
+      display: flex;
+      align-items: center;
+      font-size: 12px;
+      font-weight: 400;
+      color: #999999;
+      margin-bottom: 20rpx;
+
+      image {
+        width: 24px;
+        height: 24px;
+        margin-right: 2px;
+      }
+
+      .link {
+        color: $main-color;
+      }
+    }
+  }
+</style>

+ 141 - 0
src/pages/login/components/other-way-box.vue

@@ -0,0 +1,141 @@
+<template>
+  <view class="other-way-box">
+    <view class="title">
+      <span style="margin: 0 10px">—— 第三方账号登录 ——</span>
+    </view>
+    <view class="other-way">
+      <!-- 手机号登录 -->
+      <view v-if="phoneLoginFlag" @click="navigateTo('/pages/login/phone-login')" class="item">
+        <image src="@/static/images/login/phone-login-icon.png" />
+      </view>
+      <!-- 微信登录 -->
+      <view v-if="wxLoginFlag" @click="toWeChatLogin" class="item">
+        <image src="@/static/images/login/wx-login-icon.png" />
+      </view>
+      <!-- 苹果账号登录 -->
+      <view v-if="iosFlag" @click="toAppleLogin" class="item apple">
+        <image src="@/static/images/login/ios-login-icon.png" />
+      </view>
+    </view>
+  </view>
+</template>
+<script setup>
+  import { computed } from 'vue';
+
+  const emit = defineEmits(['wechatLogin', 'appleLogin']);
+
+  const phoneLoginFlag = computed(() => {
+    let currentPages = getCurrentPages();
+    let currentPage = currentPages[currentPages.length - 1];
+    return currentPage.route === 'pages/login/phone-login';
+  });
+  const wxLoginFlag = computed(() => {
+    let currentPages = getCurrentPages();
+    let currentPage = currentPages[currentPages.length - 1];
+    return currentPage.route === 'pages/login/login';
+  });
+  const iosFlag = computed(() => {
+    // #ifdef APP-PLUS
+    let systemInfoSync = uni.getSystemInfoSync();
+    let platform = systemInfoSync.platform;
+    return platform === 'ios';
+    // #endif
+    return true;
+  });
+
+  function navigateTo(url) {
+    uni.navigateTo({ url });
+  }
+  // 微信登录
+  function toWeChatLogin() {
+    emit('wechatLogin');
+  }
+
+  // 苹果登录
+  function toAppleLogin() {
+    //方法二
+    var appleOauth = null;
+    plus.oauth.getServices(
+      (services) => {
+        for (var i in services) {
+          var service = services[i];
+          // 获取苹果授权登录对象,苹果授权登录id 为 'apple' iOS13以下系统,不会返回苹果登录对应的 service
+          if (service.id == 'apple') {
+            appleOauth = service;
+            break;
+          }
+        }
+        appleOauth.logout(
+          (success) => {
+            // console.log("退出登录成功:", JSON.stringify(success))
+
+            appleOauth.login(
+              (oauth) => {
+                // 授权成功,苹果授权返回的信息在 oauth.target.appleInfo 中
+                let info = oauth.target.appleInfo || {};
+                // console.log("登录数据:", JSON.stringify(info))
+                emit('appleLogin', info);
+              },
+              (err) => {
+                // 授权失败 error
+              },
+              {
+                // 默认只会请求用户名字信息,如需请求用户邮箱信息,需要设置 scope: 'email'
+                scope: 'email',
+              }
+            );
+          },
+          (err) => {
+            console.log('退出登录失败');
+          }
+        );
+      },
+      (err) => {
+        // 获取 services 失败
+      }
+    );
+  }
+</script>
+<style lang="scss" scoped>
+  .other-way-box {
+    flex-shrink: 0;
+    margin-top: 82rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: column;
+
+    .title {
+      font-size: $small-size;
+      font-weight: 400;
+      color: #cccccc;
+    }
+
+    .other-way {
+      margin-top: 42rpx;
+      height: 80rpx;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .item {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        flex-direction: column;
+        font-size: $main-size;
+        font-weight: 400;
+        color: #666666;
+
+        &.apple {
+          margin-left: 40px;
+        }
+
+        image {
+          width: 70rpx;
+          height: 70rpx;
+        }
+      }
+    }
+  }
+</style>

+ 366 - 0
src/pages/login/login.vue

@@ -0,0 +1,366 @@
+<template>
+  <view class="container">
+    <view class="top-view">
+      <view class="login"> 登录 </view>
+      <view class="logo">
+        <image src="@/static/images/login/login-logo.png" />
+      </view>
+    </view>
+    <view class="bottom-view">
+      <view class="input-view smart-margin-top10">
+        <image src="@/static/images/login/login-username.png"></image>
+        <uni-easyinput
+          class="input"
+          placeholder="请输入用户名"
+          :clearable="true"
+          placeholderStyle="color:#CCCCCC"
+          border="none"
+          v-model="loginForm.loginName"
+        />
+      </view>
+
+      <view class="input-view smart-margin-top10" v-if="emailCodeShowFlag">
+        <image src="@/static/images/login/login-password.png"></image>
+        <uni-easyinput
+          class="input"
+          placeholder="请输入邮箱验证码"
+          :clearable="true"
+          placeholderStyle="color:#CCCCCC"
+          border="none"
+          v-model="loginForm.emailCode"
+        />
+        <button @click="sendSmsCode" class="code-btn" :disabled="emailCodeButtonDisabled">
+              {{ emailCodeTips }}
+        </button>
+      </view>
+
+      <view class="input-view smart-margin-top10">
+        <image src="@/static/images/login/login-password.png"></image>
+        <uni-easyinput
+          class="input"
+          placeholder="请输入密码"
+          :clearable="true"
+          :password="true"
+          placeholderStyle="color:#CCCCCC"
+          border="none"
+          v-model="loginForm.password"
+        />
+      </view>
+
+      <view class="input-view smart-margin-top10">
+        <image src="@/static/images/login/login-password.png"></image>
+        <uni-easyinput
+          class="input captcha-input"
+          placeholder="请输入验证码"
+          :clearable="true"
+          :password="false"
+          placeholderStyle="color:#CCCCCC"
+          border="none"
+          v-model="loginForm.captchaCode"
+        />
+        <img class="captcha-img" :src="captchaBase64Image" @click="getCaptcha" />
+      </view>
+
+      <view class="code-login-view smart-margin-top10">
+        <text class="code-text">验证码登录</text>
+        <text class="forget-text">忘记密码?</text>
+      </view>
+
+      <view @click="login" class="button login-btn smart-margin-top20"> 登录 </view>
+      <view @click="login" class="button register-btn smart-margin-top20"> 创建账号 </view>
+      <OtherWayBox />
+      <LoginCheckBox class="login-check-box" ref="loginCheckBoxRef" />
+    </view>
+  </view>
+</template>
+<script setup>
+  import { reactive, ref } from 'vue';
+  import { onShow } from '@dcloudio/uni-app';
+  import OtherWayBox from './components/other-way-box.vue';
+  import LoginCheckBox from './components/login-check-box.vue';
+  import { loginApi } from '@/api/system/login-api';
+  import { LOGIN_DEVICE_ENUM } from '@/constants/system/login-device-const';
+  import { encryptData } from '@/lib/encrypt';
+  import { useUserStore } from '@/store/modules/system/user';
+  import { smartSentry } from '@/lib/smart-sentry';
+
+  const loginForm = reactive({
+    loginName: 'admin',
+    password: '123456',
+    captchaCode: '',
+    captchaUuid: '',
+    loginDevice: LOGIN_DEVICE_ENUM.H5.value,
+  });
+
+  const loginCheckBoxRef = ref();
+  async function login() {
+    if (!loginCheckBoxRef.value.agreeFlag) {
+      uni.showToast({
+        icon: 'none',
+        title: '请阅读并同意《用户协议》、《隐私政策》',
+      });
+      return;
+    }
+    if (!loginForm.loginName) {
+      uni.showToast({
+        icon: 'none',
+        title: '请输入用户名',
+      });
+      return;
+    }
+    if (!loginForm.password) {
+      uni.showToast({
+        icon: 'none',
+        title: '请输入密码',
+      });
+      return;
+    }
+
+    try {
+      uni.showLoading({ title: '登录中' });
+      // 密码加密
+      let encryptPasswordForm = Object.assign({}, loginForm, {
+        password: encryptData(loginForm.password),
+      });
+      const res = await loginApi.login(encryptPasswordForm);
+      stopRefreshCaptchaInterval();
+      uni.showToast({ title: '登录成功' });
+      //更新用户信息到 pinia
+      useUserStore().setUserLoginInfo(res.data);
+
+      uni.switchTab({ url: '/pages/home/index' });
+    } catch (e) {
+      if (e.data && e.data.code !== 0) {
+        loginForm.captchaCode = '';
+        getCaptcha();
+      }
+      smartSentry.captureError(e);
+      uni.hideLoading();
+    }
+  }
+
+  //--------------------- 验证码 ---------------------------------
+
+  const captchaBase64Image = ref('');
+
+  async function getCaptcha() {
+    try {
+      let captchaResult = await loginApi.getCaptcha();
+      captchaBase64Image.value = captchaResult.data.captchaBase64Image;
+      console.log(captchaResult.data.captchaBase64Image, 2);
+      loginForm.captchaUuid = captchaResult.data.captchaUuid;
+      beginRefreshCaptchaInterval(captchaResult.data.expireSeconds);
+    } catch (e) {
+      console.log(e);
+    }
+  }
+
+  let refreshCaptchaInterval = null;
+
+  function beginRefreshCaptchaInterval(expireSeconds) {
+    if (refreshCaptchaInterval === null) {
+      refreshCaptchaInterval = setInterval(getCaptcha, (expireSeconds - 5) * 1000);
+    }
+  }
+
+  function stopRefreshCaptchaInterval() {
+    if (refreshCaptchaInterval != null) {
+      clearInterval(refreshCaptchaInterval);
+      refreshCaptchaInterval = null;
+    }
+  }
+
+  const emailCodeShowFlag = ref(false);
+  let emailCodeTips = ref('获取邮箱验证码');
+  let emailCodeButtonDisabled = ref(false);
+  // 定时器
+  let countDownTimer = null;
+  // 开始倒计时
+  function runCountDown() {
+    emailCodeButtonDisabled.value = true;
+    let countDown = 60;
+    emailCodeTips.value = `${countDown}秒后重新获取`;
+    countDownTimer = setInterval(() => {
+      if (countDown > 1) {
+        countDown--;
+        emailCodeTips.value = `${countDown}秒后重新获取`;
+      } else {
+        clearInterval(countDownTimer);
+        emailCodeButtonDisabled.value = false;
+        emailCodeTips.value = '获取验证码';
+      }
+    }, 1000);
+  }
+
+  // 获取双因子登录标识
+  async function getTwoFactorLoginFlag() {
+    try {
+      let result = await loginApi.getTwoFactorLoginFlag();
+      emailCodeShowFlag.value = result.data;
+    } catch (e) {
+      smartSentry.captureError(e);
+    }
+  }
+  // 发送邮箱验证码
+  async function sendSmsCode() {
+  try {
+    uni.showLoading();
+    let result = await loginApi.sendLoginEmailCode(loginForm.loginName);
+    message.success('验证码发送成功!请登录邮箱查看验证码~');
+    runCountDown();
+  } catch (e) {
+    smartSentry.captureError(e);
+  } finally {
+    uni.hideLoading();
+  }
+  }
+  onShow(()=>{
+    getCaptcha()
+    getTwoFactorLoginFlag();
+  });
+</script>
+<style lang="scss" scoped>
+  .bottom-view {
+    box-sizing: border-box;
+    margin-top: -280rpx;
+    border-radius: 20rpx 20rpx 0 0;
+    width: 100%;
+    background-color: white;
+    padding: 0 60rpx;
+    .input-view {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      background-color: $page-bg-color;
+      border-radius: 4px;
+      height: 100rpx;
+      .captcha-img {
+        margin-left: 5px;
+        height: 100rpx;
+        width: 40%;
+      }
+      image {
+        margin-left: 30rpx;
+        width: 44rpx;
+        height: 44rpx;
+      }
+      .input {
+        margin: 0 16rpx;
+        background-color: $page-bg-color;
+      }
+      .captcha-input {
+        width: 50%;
+      }
+    }
+    .code-login-view {
+      margin: 50rpx 0 0;
+      height: 40rpx;
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: space-between;
+      .code-text {
+        height: 40rpx;
+        font-size: $main-size;
+        font-weight: 400;
+        text-align: left;
+        color: $main-font-color;
+      }
+      .forget-text {
+        height: 40rpx;
+        font-size: $main-size;
+        font-weight: 400;
+        text-align: right;
+        color: $second-font-color;
+      }
+    }
+  }
+  .button {
+    flex-shrink: 0;
+    width: 100%;
+    height: 90rpx;
+    border-radius: 4px;
+    box-shadow: 0px 5px 8px 0px rgba(58, 121, 255, 0.2);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: $main-size;
+
+    &.disabled {
+      opacity: 0.4;
+    }
+    &.login-btn {
+      background: $main-color;
+      color: #ffffff;
+    }
+
+    &.register-btn {
+      background: white;
+      color: $main-color;
+      border: 0.5px solid $main-color;
+      border-color: rgba(26, 154, 255, 0.3);
+    }
+  }
+
+  .logo {
+    flex-shrink: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: row;
+    height: 220rpx;
+
+    image {
+      width: 208rpx;
+      height: 220rpx;
+    }
+  }
+
+  ::v-deep .uni-easyinput__content {
+    background-color: transparent !important;
+  }
+  ::v-deep .is-input-border {
+    border: none;
+  }
+  .container {
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+    min-height: 100vh;
+    width: 100vw;
+    .back-icon {
+      width: 18px;
+      height: 18px;
+    }
+    .top-view {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      width: 100%;
+      height: 720rpx;
+      background-image: url('~@/static/images/login/login-top-back.png');
+      .login {
+        font-weight: bold;
+        margin-top: 70rpx;
+      }
+      .logo {
+        width: 260rpx;
+        height: 260rpx;
+      }
+    }
+  }
+
+  .login-check-box {
+    flex-shrink: 0;
+    margin-top: 150rpx;
+    margin-bottom: 120rpx;
+    align-self: flex-start;
+  }
+  .code-btn{
+    width: 240rpx;
+    font-size: 24rpx;
+    margin-right: 20rpx;
+    background-color: $main-color;
+    color: #fff;
+  }
+</style>

+ 166 - 0
src/pages/message/message.vue

@@ -0,0 +1,166 @@
+<template>
+  <view class="container">
+    <mescroll-body @init="mescrollInit" :down="{ auto: false }" @down="onDown" @up="onUp">
+      <mescroll-empty v-if="messageListData.length === 0"></mescroll-empty>
+      <view class="message" v-for="(item, index) in messageListData">
+        <view class="message-header">
+          <view class="header-left">
+            <image src="/src/static/images/message/message.png" mode=""></image>
+            <view>
+              {{ $smartEnumPlugin.getDescByValue('MESSAGE_TYPE_ENUM', item.messageType) }}
+            </view>
+          </view>
+          <view class="header-time"> {{item.createTime}} </view>
+        </view>
+        <view class="content">
+          <view class="message-title">
+            <uni-icons v-if="!item.readFlag" color="red" class="smart-margin-right10" type="info-filled" :size="14"></uni-icons>
+            {{ item.title }}
+          </view>
+          <view class="message-body"> {{item.content}} </view>
+        </view>
+      </view>
+    </mescroll-body>
+  </view>
+</template>
+
+<script setup>
+  import { reactive, ref } from 'vue';
+  import { onPageScroll, onReachBottom } from '@dcloudio/uni-app';
+  import useMescroll from '@/uni_modules/uni-mescroll/hooks/useMescroll';
+  import { smartSentry } from '@/lib/smart-sentry';
+  import { messageApi } from '@/api/support/message-api';
+
+  // --------------------------- 查询 ---------------------------------
+
+  const defaultForm = {
+    searchWord: '',
+    messageType: null,
+    dataId: null,
+    readFlag: null,
+    endDate: null,
+    startDate: null,
+    pageNum: 1,
+    pageSize: 10,
+    searchCount: true,
+    receiverType: null,
+    receiverId: null,
+  };
+
+  // 查询表单
+  const queryForm = reactive({ ...defaultForm });
+  // 通知列表数据
+  const messageListData = ref([]);
+
+  function buildQueryParam(pageNum) {
+    queryForm.pageNum = pageNum;
+    return Object.assign({}, queryForm, { pageNum });
+  }
+
+  async function query(mescroll, isDownFlag, param) {
+    try {
+      let res = await messageApi.queryMessage(param);
+      res.data.list.map(e => e.content = e.content.substr(0,50));
+      if (!isDownFlag) {
+        messageListData.value = messageListData.value.concat(res.data.list);
+      } else {
+        messageListData.value = res.data.list;
+      }
+      mescroll.endSuccess(res.data.list.length, res.data.pages > res.data.pageNum);
+    } catch (e) {
+      smartSentry.captureError(e);
+      //联网失败, 结束加载
+      mescroll.endErr();
+    }
+  }
+
+  const { mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
+
+  /**
+   * 搜索
+   */
+  function search() {
+    query(getMescroll(), true, buildQueryParam(1));
+    uni.pageScrollTo({
+      scrollTop: 0,
+    });
+  }
+
+  /**
+   * 下拉刷新
+   */
+  function onDown(mescroll) {
+    queryForm.pageNum = 1;
+    query(mescroll, true, buildQueryParam(1));
+  }
+
+  /**
+   * 上拉加载更多
+   */
+  function onUp(mescroll) {
+    query(mescroll, false, buildQueryParam(mescroll.num));
+  }
+</script>
+
+<style lang="scss" scoped>
+  .message {
+    width: 700rpx;
+    background: #ffffff;
+    border-radius: 12rpx;
+    box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+    margin: 24rpx auto 0;
+    box-sizing: border-box;
+    padding: 30rpx 30rpx 24rpx;
+    .message-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 24rpx;
+      .header-left {
+        align-items: center;
+        display: flex;
+        image {
+          width: 48rpx;
+          height: 48rpx;
+          margin-right: 14rpx;
+        }
+        font-size: 34rpx;
+        color: #000000;
+      }
+      .header-time {
+        font-size: 28rpx;
+        font-weight: 400;
+        color: #999999;
+      }
+    }
+    .content {
+      box-sizing: border-box;
+      padding: 24rpx;
+      background-color: #f7f8f9;
+      width: 100%;
+      border-radius: 8rpx;
+      .message-title {
+        color: #323333;
+        font-size: 34rpx;
+        font-weight: bold;
+        margin-bottom: 8rpx;
+        display: flex;
+        flex-direction: row;
+        justify-content: flex-start;
+        align-items: center;
+        white-space: nowrap; /* 确保文本不会换行 */
+        overflow: hidden; /* 隐藏溢出的内容 */
+        text-overflow: ellipsis; /* 在文本溢出处显示省略号 */
+      }
+      .message-body {
+        font-size: 28rpx;
+        max-height: 3.2rem;
+        color: #777777;
+      }
+    }
+  }
+
+  page {
+    background-color: #f5f5f5;
+  }
+</style>

+ 112 - 0
src/pages/mine/components/mine-menu.vue

@@ -0,0 +1,112 @@
+<template>
+  <view>
+    <view class="menu-list">
+      <uni-list>
+        <uni-list-item title="切换样式" link showBadge @click="changeStyle">
+          <template #header>
+            <image class="icon" src="/static/images/mine/mine-account.png" mode=""></image>
+          </template>
+        </uni-list-item>
+        <uni-list-item title="消息通知" link="switchTab" showBadge :badgeText="messageCount" badgeType="error" to="/pages/message/message">
+          <template #header>
+            <image class="icon" src="/static/images/mine/mine-message.png" mode=""></image>
+          </template>
+        </uni-list-item>
+
+        <uni-list-item title="意见反馈" link rightText="欢迎吐槽" showBadge to="/pages/support/feedback/feedback-form">
+          <template #header>
+            <image class="icon" src="/static/images/mine/mine-feedback.png" mode=""></image>
+          </template>
+        </uni-list-item>
+        <uni-list-item title="联系客服" showBadge clickable @click="callService">
+          <template #header>
+            <image class="icon" src="/static/images/mine/mine-service.png" mode=""></image>
+          </template>
+          <template #footer>
+            <view style="font-size: 30rpx; color: #1a9aff; line-height: 45rpx"> 18810241024 </view>
+          </template>
+        </uni-list-item>
+        <uni-list-item title="账号管理" showBadge link>
+          <template #header>
+            <image class="icon" src="/static/images/mine/mine-account.png" mode=""></image>
+          </template>
+        </uni-list-item>
+      </uni-list>
+    </view>
+
+    <view class="menu-list">
+      <uni-list>
+        <uni-list-item title="关于我们" link showBadge>
+          <template #header>
+            <image class="icon" src="/static/images/mine/mine-about-us.png" mode=""></image>
+          </template>
+        </uni-list-item>
+        <uni-list-item title="版本信息" link rightText="V1.0" showBadge>
+          <template #header>
+            <image class="icon" src="/static/images/mine/mine-version-info.png" mode=""></image>
+          </template>
+        </uni-list-item>
+        <uni-list-item title="隐私条款" link showBadge>
+          <template #header>
+            <image class="icon" src="/static/images/mine/user-agreement-icon.png" mode=""></image>
+          </template>
+        </uni-list-item>
+        <uni-list-item title="用户协议" link showBadge>
+          <template #header>
+            <image class="icon" src="/static/images/mine/mine-about-us.png" mode=""></image>
+          </template>
+        </uni-list-item>
+        <uni-list-item title="设置" showBadge clickable @click="developing">
+          <template #header>
+            <image class="icon" src="/static/images/mine/mine-protocol.png"></image>
+          </template>
+        </uni-list-item>
+      </uni-list>
+    </view>
+  </view>
+</template>
+<script setup>
+  import { SmartToast } from '@/lib/smart-support';
+  import { useUserStore } from '@/store/modules/system/user';
+  import { computed } from 'vue';
+
+  const emits = defineEmits(['changeStyle']);
+  const userStore = useUserStore();
+  const messageCount = computed(() => userStore.$state.unreadMessageCount);
+
+  function changeStyle() {
+    emits('changeStyle');
+  }
+
+  function developing() {
+    SmartToast.toast('敬请期待');
+  }
+
+  function callService() {
+    uni.makePhoneCall({
+      phoneNumber: '18637925892',
+    });
+  }
+</script>
+<style scoped lang="scss">
+  .menu-list {
+    box-sizing: border-box;
+    background-color: #fff;
+    margin: 16px 12px 0 12px;
+    border-radius: 6px;
+    overflow: hidden;
+    box-shadow: 0 3px 4px 0 rgba(24, 144, 255, 0.06);
+    padding: 0 10rpx;
+    :deep(.uni-list-item__content-title) {
+      font-size: 30rpx;
+    }
+    :deep(.uni-list-item__extra-text) {
+      font-size: 28rpx;
+    }
+    .icon {
+      width: 56rpx;
+      height: 56rpx;
+      margin-right: 20rpx;
+    }
+  }
+</style>

+ 111 - 0
src/pages/mine/components/mine-user-blue.vue

@@ -0,0 +1,111 @@
+<template>
+  <!-- 顶部背景 -->
+  <view class="nav-container">
+    <view class="title"> 我的 </view>
+  </view>
+  <!-- 用户信息 -->
+  <view class="user-info-box">
+    <view class="user-icon">
+      <image src="https://img.smartadmin.1024lab.net/hexagon/logo.png" class="user-image"> </image>
+    </view>
+    <view class="user-info">
+      <view class="user-name">{{ actualName }}</view>
+      <view class="user-phone">{{ departmentName }}</view>
+    </view>
+    <view class="vip-flag">
+      <image src="@/static/images/mine/no-vip-flag.png" mode=""></image>
+    </view>
+  </view>
+</template>
+<script setup>
+  import { useUserStore } from '@/store/modules/system/user';
+  import { computed } from 'vue';
+
+  const actualName = computed(() => {
+    return useUserStore().actualName;
+  });
+  const phone = computed(() => {
+    return useUserStore().phone;
+  });
+  const departmentName = computed(() => {
+    return useUserStore().departmentName;
+  });
+</script>
+<style scoped lang="scss">
+  .nav-container {
+    flex-shrink: 0;
+    width: 100%;
+    height: 200rpx;
+    background: url('@/static/images/mine/top-background.png') center/100% no-repeat;
+    position: relative;
+    .title {
+      text-align: center;
+      margin-top: 30rpx;
+      font-size: 36rpx;
+      color: #fff;
+      font-weight: bold;
+    }
+  }
+
+  .user-info-box {
+    z-index: 10;
+    margin: -60rpx 12px 0;
+    background: #ffffff;
+    border-radius: 6px;
+    box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+    backdrop-filter: blur(5.98px);
+    padding: 40rpx 30rpx;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    .user-icon {
+      width: 120rpx;
+      height: 120rpx;
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: center;
+      border-radius: 60rpx;
+      background-color: white;
+      .user-image {
+        width: 108rpx;
+        height: 108rpx;
+        border-radius: 54rpx;
+      }
+    }
+    .user-info {
+      display: flex;
+      flex-direction: column;
+      align-items: flex-start;
+      flex-shrink: 0;
+      margin: 10rpx auto 0 24rpx;
+      align-self: flex-start;
+      .user-name {
+        height: 50rpx;
+        font-size: 40rpx;
+        font-weight: 600;
+        text-align: center;
+        color: #323333;
+      }
+      .user-phone {
+        margin-top: 8rpx;
+        height: 40rpx;
+        font-size: 30rpx;
+        font-weight: 400;
+        text-align: left;
+        color: #777777;
+        line-height: 40rpx;
+      }
+    }
+    .vip-flag {
+      align-self: flex-start;
+      width: 170rpx;
+      height: 60rpx;
+      margin: 6rpx 0 0 auto;
+      image {
+        width: 100%;
+        height: 100%;
+      }
+    }
+  }
+</style>

+ 175 - 0
src/pages/mine/components/mine-user-white.vue

@@ -0,0 +1,175 @@
+<template>
+  <view class="user-info-box">
+    <view class="user-icon">
+      <image class="user-image" src="https://img.smartadmin.1024lab.net/hexagon/logo.png"> </image>
+    </view>
+    <view class="user-info">
+      <view class="user-name">{{ actualName }}</view>
+      <view class="user-phone">{{ departmentName }}</view>
+    </view>
+  </view>
+
+  <view class="vip-card">
+    <view class="card-left">
+      <image class="vip-icon" src="/static/images/mine/vip-icon.png" mode=""></image>
+      <view class=""> SmartAdmin </view>
+    </view>
+    <image class="open-vip" src="/static/images/mine/open-vip.png" mode=""></image>
+  </view>
+
+  <view class="grid-menu">
+    <uni-grid :column="4" :showBorder="false">
+      <uni-grid-item v-for="(item, index) in menuList" :index="index" :key="index">
+        <view class="grid-item-box" style="background-color: #fff">
+          <image class="image" :src="item.image" mode=""></image>
+          <text class="text">{{ item.title }}</text>
+        </view>
+      </uni-grid-item>
+    </uni-grid>
+  </view>
+</template>
+<script setup>
+  import { useUserStore } from '@/store/modules/system/user';
+  import { computed } from 'vue';
+
+  const actualName = computed(() => {
+    return useUserStore().actualName;
+  });
+  const phone = computed(() => {
+    return useUserStore().phone;
+  });
+  const departmentName = computed(() => {
+    return useUserStore().departmentName;
+  });
+  const menuList = [
+    {
+      title: '地址',
+      image: '/static/images/mine/mine-menu-address.png',
+    },
+    {
+      title: '优惠券',
+      image: '/static/images/mine/mine-menu-coupon.png',
+    },
+    {
+      title: '收藏',
+      image: '/static/images/mine/mine-menu-collect.png',
+    },
+    {
+      title: '余额',
+      image: '/static/images/mine/mine-menu-balance.png',
+    },
+  ];
+</script>
+<style scoped lang="scss">
+  .user-info-box {
+    z-index: 10;
+    padding: 60rpx 24rpx 30rpx;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    .user-icon {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: center;
+      border-radius: 60rpx;
+      background-color: white;
+      .user-image {
+        width: 120rpx;
+        height: 120rpx;
+        border-radius: 54rpx;
+      }
+    }
+    .user-info {
+      display: flex;
+      flex-direction: column;
+      align-items: flex-start;
+      flex-shrink: 0;
+      margin: 10rpx auto 0 24rpx;
+      align-self: flex-start;
+      .user-name {
+        height: 50rpx;
+        font-size: 40rpx;
+        font-weight: 600;
+        text-align: center;
+        color: #323333;
+      }
+      .user-phone {
+        margin-top: 8rpx;
+        height: 40rpx;
+        font-size: 30rpx;
+        font-weight: 400;
+        text-align: left;
+        color: #777777;
+        line-height: 40rpx;
+      }
+    }
+  }
+
+  .vip-card {
+    background-image: url('~@/static/images/mine/vip-bg.png');
+    height: 80rpx;
+    background-repeat: no-repeat;
+    width: 700rpx;
+    margin: 0 auto;
+    background-size: 700rpx 80rpx;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    .card-left {
+      display: flex;
+      align-items: center;
+      .vip-icon {
+        width: 50rpx;
+        height: 46rpx;
+        margin-left: 32rpx;
+        margin-right: 8rpx;
+      }
+      view {
+        color: #f5cc8f;
+        font-size: 30rpx;
+        font-weight: 600;
+      }
+    }
+
+    .open-vip {
+      width: 136rpx;
+      height: 46rpx;
+      display: flex;
+      margin-right: 32rpx;
+    }
+  }
+
+  .grid-menu {
+    width: 700rpx;
+    margin: 20rpx auto;
+  }
+  .menu-item {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    .item-image {
+      width: 80rpx;
+      height: 80rpx;
+    }
+  }
+
+  .image {
+    width: 84rpx;
+    height: 84rpx;
+  }
+
+  .text {
+    font-size: 30rpx;
+    margin-top: 10rpx;
+  }
+  .grid-item-box {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    padding: 30rpx 0;
+  }
+</style>

+ 66 - 0
src/pages/mine/mine.vue

@@ -0,0 +1,66 @@
+<template>
+  <view class="container">
+    <!-- 用户 -->
+    <MineUserBlue v-if="blueUserFlag" />
+    <MineUserWhite v-if="!blueUserFlag" />
+
+    <!---功能菜单--->
+    <MineMenu @change-style="onChangeStyle" />
+
+    <!---退出--->
+    <view class="logout-btn" @click="logout">
+      <view class="label">退出登录</view>
+    </view>
+  </view>
+</template>
+<script setup>
+  import MineMenu from './components/mine-menu.vue';
+  import MineUserBlue from './components/mine-user-blue.vue';
+  import MineUserWhite from './components/mine-user-white.vue';
+  import { ref } from 'vue';
+  import { useUserStore } from '@/store/modules/system/user';
+  import { SmartLoading, SmartToast } from '@/lib/smart-support';
+  import { smartSentry } from '@/lib/smart-sentry';
+
+  const userStore = useUserStore();
+  const blueUserFlag = ref(true);
+  function onChangeStyle() {
+    blueUserFlag.value = !blueUserFlag.value;
+  }
+
+  async function logout() {
+    try {
+      setTimeout(() => {
+        userStore.logout();
+        uni.navigateTo({ url: '/pages/login/login' });
+      }, 500);
+      await SmartLoading.show();
+      SmartToast.toast('退出成功');
+    } catch (e) {
+      smartSentry.captureError(e);
+    } finally {
+      SmartLoading.hide();
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .container {
+    width: 100%;
+    background: #f4f4f4;
+    display: flex;
+    flex-direction: column;
+  }
+
+  .logout-btn {
+    margin: 24px 27px 50px;
+    height: 44px;
+    opacity: 0.5;
+    background: $uni-color-error;
+    border-radius: 22px;
+    font-size: 15px;
+    line-height: 44px;
+    font-weight: 700;
+    text-align: center;
+    color: white;
+  }
+</style>

+ 72 - 0
src/pages/notice/components/notice-list.vue

@@ -0,0 +1,72 @@
+<template>
+  <view class="list-container">
+    <view class="list-item" @click="gotoDetail(item.noticeId)" v-for="item in list" :key="item.noticeId">
+      <view class="title"> {{ item.noticeTypeName }} : {{ item.title }} </view>
+      <view class="author" v-if="item.author"> 作者:{{ item.author }} </view>
+      <view class="author" v-if="item.source"> 来源:{{ item.source }} </view>
+      <view class="publish-time">
+        <view class="time">发布时间:{{ item.publishTime }}</view>
+        <view class="un-read" v-if="!item.viewFlag">未读</view>
+        <view class="read" v-if="item.viewFlag">已读</view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  const props = defineProps({
+    marginTop: {
+      type: String,
+      default: '0',
+    },
+    list: {
+      type: Array,
+      default: [],
+    },
+  });
+
+  function gotoDetail(noticeId) {
+    uni.navigateTo({ url: '/pages/notice/notice-detail?noticeId=' + noticeId });
+  }
+</script>
+
+<style lang="scss" scoped>
+  .list-container {
+    margin-top: v-bind(marginTop);
+    padding: 20rpx 10rpx;
+  }
+  .list-item {
+    box-sizing: border-box;
+    margin: 0 30rpx;
+    padding: 30rpx 0;
+    border-bottom: #cdcdcd 1px solid;
+
+    .title {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 20rpx;
+      font-size: 16px;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      overflow: hidden;
+    }
+    .author {
+      margin-bottom: 14rpx;
+      color: #999999;
+      font-size: 12px;
+      text-overflow: ellipsis;
+    }
+    .publish-time {
+      font-size: 12px;
+      color: #999999;
+      display: flex;
+      flex-direction: row;
+      justify-content: space-between;
+
+      .un-read {
+        color: $uni-color-primary;
+      }
+    }
+  }
+</style>

+ 115 - 0
src/pages/notice/components/notice-query-form-popup.vue

@@ -0,0 +1,115 @@
+<template>
+  <uni-popup ref="popupRef" background-color="#fff" type="bottom" :is-mask-click="false">
+    <view class="query-form-pop">
+      <view class="smart-form">
+        <uni-forms :label-width="100" :modelValue="form" label-position="left">
+          <view class="smart-form-group">
+            <view class="smart-form-group-title"> 分类 </view>
+            <view class="smart-form-group-content">
+              <uni-forms-item class="smart-form-item" label="类型:">
+                <uni-data-select v-model="form.noticeTypeId" :localdata="noticeTypeList" @change="changeNoticeType" :clear="true" />
+              </uni-forms-item>
+            </view>
+          </view>
+
+          <view class="smart-form-group">
+            <view class="smart-form-group-title"> 发布日期 </view>
+            <view class="smart-form-group-content">
+              <uni-forms-item class="smart-form-item" label="开始日期">
+                <uni-datetime-picker type="date" clear-icon v-model="form.publishTimeBegin" />
+              </uni-forms-item>
+              <uni-forms-item class="smart-form-item" label="截止日期">
+                <uni-datetime-picker type="date" clear-icon v-model="form.publishTimeEnd" />
+              </uni-forms-item>
+            </view>
+          </view>
+        </uni-forms>
+
+        <view class="smart-form-submit smart-margin-top20">
+          <button class="smart-form-submit-btn smart-margin-right20" type="default" @click="cancel">取消</button>
+          <button class="smart-form-submit-btn" type="warn" @click="reset">重置</button>
+          <button class="smart-form-submit-btn" type="primary" @click="ok">确定</button>
+        </view>
+      </view>
+    </view>
+  </uni-popup>
+</template>
+
+<script setup>
+  import { onMounted, reactive, ref, toRaw } from 'vue';
+  import { noticeApi } from '@/api/business/oa/notice-api';
+  import _ from 'lodash';
+
+  const emits = defineEmits(['close']);
+  defineExpose({ show });
+
+  // --------------------- 显示 隐藏 ---------------------
+  const popupRef = ref();
+
+  function show() {
+    popupRef.value.open();
+  }
+
+  function close() {
+    popupRef.value.close();
+  }
+
+  // --------------------- 表单 ---------------------
+
+  const defaultFormData = {
+    // 分类
+    noticeTypeId: undefined,
+    noticeTypeName: undefined,
+    // 发布开始时间
+    publishTimeBegin: undefined,
+    // 发布截止时间
+    publishTimeEnd: undefined,
+  };
+
+  const form = reactive({ ...defaultFormData });
+
+  // --------------------- 通知类型 ---------------------
+
+  const noticeTypeList = ref([]);
+  async function queryNoticeTypeList() {
+    let res = await noticeApi.getAllNoticeTypeList();
+    noticeTypeList.value = res.data.map((e) => Object.assign({}, { text: e.noticeTypeName, value: e.noticeTypeId }));
+  }
+
+  onMounted(() => {
+    queryNoticeTypeList();
+  });
+
+  function changeNoticeType(e) {
+    form.noticeTypeId = e;
+    form.noticeTypeName = _.find(noticeTypeList.value, { value: form.noticeTypeId }).text;
+  }
+
+  // ----------------------- 表单操作 ------------------------
+
+  // 取消
+  function cancel() {
+    close();
+    emits('close', null);
+  }
+
+  // 重置
+  function reset() {
+    Object.assign(form, defaultFormData);
+    close();
+    emits('close', toRaw(form));
+  }
+
+  // 确定
+  function ok() {
+    close();
+    emits('close', toRaw(form));
+  }
+</script>
+
+<style lang="scss" scoped>
+  .query-form-pop {
+    height: 800rpx;
+    overflow-y: scroll;
+  }
+</style>

+ 76 - 0
src/pages/notice/notice-detail.vue

@@ -0,0 +1,76 @@
+<template>
+  <view class="container">
+    <view class="title">
+      <uni-title type="h1" align="center" :title="noticeDetail.title"></uni-title>
+      <uni-title
+        type="h4"
+        align="center"
+        color="#999999"
+        v-if="noticeDetail.documentNumber"
+        :title="'(' + noticeDetail.documentNumber + ')'"
+      ></uni-title>
+      <uni-title type="h4" align="center" color="#999999" :title="noticeDetail.subTitle"></uni-title>
+    </view>
+    <view class="content">
+      <rich-text :nodes="noticeDetail.content" />
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import { reactive } from 'vue';
+  import { noticeApi } from '@/api/business/oa/notice-api';
+  import { onLoad } from '@dcloudio/uni-app';
+  import { smartSentry } from '@/lib/smart-sentry';
+
+  const noticeDetail = reactive({
+    title: '',
+    subTitle: '',
+    documentNumber: '',
+    content: '',
+  });
+
+  async function getNoticeDetail(noticeId) {
+    try {
+      uni.showLoading({ title: '加载中' });
+      let res = await noticeApi.view(noticeId);
+      noticeDetail.title = res.data.title;
+      noticeDetail.content = res.data.contentHtml;
+      noticeDetail.documentNumber = res.data.documentNumber;
+      let subTitleArray = [];
+      if (res.data.author) {
+        subTitleArray.push(res.data.author);
+      }
+      if (res.data.publishTime) {
+        subTitleArray.push(res.data.publishTime);
+      }
+      noticeDetail.subTitle = subTitleArray.join(' | ');
+    } catch (e) {
+      smartSentry.captureError(e);
+    } finally {
+      uni.hideLoading();
+    }
+  }
+
+  onLoad((option) => {
+    uni.pageScrollTo({
+      scrollTop: 0,
+    });
+    getNoticeDetail(option.noticeId);
+  });
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    height: 100vh;
+    padding: 20rpx;
+
+    .content {
+      border-top: #cccccc 1px solid;
+      margin-top: 50rpx;
+      padding: 50rpx 16rpx 50rpx 16rpx;
+      line-height: 30px;
+      color: #333333;
+    }
+  }
+</style>

+ 224 - 0
src/pages/notice/notice-index.vue

@@ -0,0 +1,224 @@
+<template>
+  <view>
+    <mescroll-body @init="mescrollInit" :down="{ auto: false }" @down="onDown" @up="onUp">
+      <!--搜索框-->
+      <uni-nav-bar :border="false" fixed :leftWidth="0" rightWidth="70px">
+        <view class="input">
+          <uni-easyinput
+            prefixIcon="search"
+            :clearable="true"
+            trim="all"
+            v-model="queryForm.keywords"
+            placeholder="搜索:标题、作者、来源等"
+            @confirm="search"
+            @clear="search"
+          />
+        </view>
+        <template #right>
+          <view class="nav-right" @click="showQueryFormPopUp">
+            <uni-icons type="settings" size="30"></uni-icons>
+            <view class="nav-right-name"> 筛选 </view>
+          </view>
+        </template>
+      </uni-nav-bar>
+
+      <!--筛选条件提示-->
+      <uni-notice-bar
+        @close="onCloseQueryFormTips"
+        v-if="showQueryFormTipsFlag"
+        class="query-bar"
+        background-color="#007aff"
+        color="white"
+        show-close
+        single
+        :text="queryFormTips"
+      />
+
+      <!-- 筛选条件表单弹窗 -->
+      <NoticeQueryFormPopUp ref="noticeQueryFormPopUpRef" @close="onCloseQueryFormPopUp" />
+
+      <!-- 列表 -->
+      <NoticeList :list="noticeListData" :margin-top="queryFormTipsMarginTop" />
+    </mescroll-body>
+  </view>
+</template>
+
+<script setup>
+  import { reactive, ref } from 'vue';
+  import NoticeQueryFormPopUp from './components/notice-query-form-popup.vue';
+  import { noticeApi } from '@/api/business/oa/notice-api';
+  import { onPageScroll, onReachBottom } from '@dcloudio/uni-app';
+  import useMescroll from '@/uni_modules/uni-mescroll/hooks/useMescroll';
+  import { smartSentry } from '@/lib/smart-sentry';
+  import NoticeList from './components/notice-list.vue';
+  import _ from 'lodash';
+
+  // --------------------------- 筛选条件弹窗 ---------------------------------
+  const noticeQueryFormPopUpRef = ref();
+
+  /**
+   * 显示 筛选弹窗
+   */
+  function showQueryFormPopUp() {
+    noticeQueryFormPopUpRef.value.show();
+  }
+
+  /**
+   * 监听 筛选弹窗 关闭
+   */
+  function onCloseQueryFormPopUp(param) {
+    if (param === null) {
+      return;
+    }
+    Object.assign(queryForm, param);
+    showOrHideQueryFormTips();
+    query(getMescroll(), true, buildQueryParam(1));
+    uni.pageScrollTo({
+      scrollTop: 0,
+    });
+  }
+
+  // --------------------------- 筛选条件tips ---------------------------------
+  const queryFormTips = ref(null);
+  const showQueryFormTipsFlag = ref(false);
+  const queryFormTipsMarginTop = ref('0px');
+
+  /**
+   * 查询提示
+   */
+  function buildQueryFormTips() {
+    let tips = null;
+    if (queryForm.keywords) {
+      tips = '搜索:' + queryForm.keywords;
+    }
+    if (queryForm.noticeTypeName) {
+      tips = tips ? tips + ',' : '';
+      tips = tips + '类型:' + queryForm.noticeTypeName;
+    }
+    if (queryForm.publishTimeBegin) {
+      tips = tips ? tips + ',' : '';
+      tips = tips + '发布开始时间:' + queryForm.publishTimeBegin;
+    }
+    if (queryForm.publishTimeEnd) {
+      tips = tips ? tips + ',' : '';
+      tips = tips + '发布截止时间:' + queryForm.publishTimeEnd;
+    }
+    return tips;
+  }
+
+  /**
+   * 显示或者隐藏tips
+   */
+  function showOrHideQueryFormTips() {
+    let tips = buildQueryFormTips();
+    queryFormTipsMarginTop.value = _.isEmpty(tips) ? '0px' : '50rpx';
+    showQueryFormTipsFlag.value = !_.isEmpty(tips);
+    queryFormTips.value = tips;
+  }
+
+  /**
+   * 关闭筛选条件 tips,清空搜索条件
+   */
+  function onCloseQueryFormTips() {
+    Object.assign(queryForm, defaultForm);
+    queryFormTipsMarginTop.value = '0px';
+    showQueryFormTipsFlag.value = false;
+    queryFormTips.value = '';
+    search();
+  }
+
+  // --------------------------- 查询 ---------------------------------
+
+  const defaultForm = {
+    noticeTypeId: undefined, //分类
+    noticeTypeName: undefined, //分类名称
+    keywords: '', //标题、作者、来源
+    publishTimeBegin: null, //发布-开始时间
+    publishTimeEnd: null, //发布-截止时间
+    pageNum: 1,
+    pageSize: 10,
+  };
+
+  // 查询表单
+  const queryForm = reactive({ ...defaultForm });
+  // 通知列表数据
+  const noticeListData = ref([]);
+
+  function buildQueryParam(pageNum) {
+    queryForm.pageNum = pageNum;
+    return Object.assign({}, queryForm, { pageNum });
+  }
+
+  async function query(mescroll, isDownFlag, param) {
+    try {
+      let res = await noticeApi.queryEmployeeNotice(param);
+      if (!isDownFlag) {
+        noticeListData.value = noticeListData.value.concat(res.data.list);
+      } else {
+        noticeListData.value = res.data.list;
+      }
+      mescroll.endSuccess(res.data.list.length, res.data.pages > res.data.pageNum);
+    } catch (e) {
+      smartSentry.captureError(e);
+      //联网失败, 结束加载
+      mescroll.endErr();
+    }
+  }
+
+  const { mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
+
+  /**
+   * 搜索
+   */
+  function search() {
+    showOrHideQueryFormTips();
+    query(getMescroll(), true, buildQueryParam(1));
+    uni.pageScrollTo({
+      scrollTop: 0,
+    });
+  }
+
+  /**
+   * 下拉刷新
+   */
+  function onDown(mescroll) {
+    queryForm.pageNum = 1;
+    query(mescroll, true, buildQueryParam(1));
+  }
+
+  /**
+   * 上拉加载更多
+   */
+  function onUp(mescroll) {
+    query(mescroll, false, buildQueryParam(mescroll.num));
+  }
+</script>
+
+<style lang="scss" scoped>
+  .input {
+    width: 100%;
+    height: 72rpx;
+    background: #f7f8f9;
+    border-radius: 4px;
+    margin: 8rpx 0;
+    display: flex;
+    align-items: center;
+  }
+
+  .nav-right {
+    width: 140px;
+    display: flex;
+    height: 88rpx;
+    flex-direction: row;
+    line-height: 88rpx;
+    .nav-right-name {
+      margin-left: 5px;
+      line-height: 88rpx;
+      font-size: 30rpx;
+    }
+  }
+
+  .query-bar {
+    position: fixed;
+  }
+</style>

+ 118 - 0
src/pages/order-detail/components/detail-model-path.vue

@@ -0,0 +1,118 @@
+<template>
+  <view class="smart-detail-card">
+    <view class="smart-detail-card-title"> 物流信息</view>
+    <view class="view-item">
+      <view class="title">
+        <view class="title-icon-text" type="default"> 提 </view>
+        <view class="title-content"> 河南省洛阳市洛龙区龙门石窟</view>
+      </view>
+      <view class="sub-title"> 河南省洛阳市洛龙区开元大道1024号1024大厦10层1024室 </view>
+      <view class="sub-title"> 2023-11-23 12:12:12,快递员:刘备(188123123123)</view>
+    </view>
+    <view class="view-item">
+      <view class="title">
+        <view class="title-icon-text" type="warning"> 装 </view>
+        <view class="title-content"> 山东省济南市高新区齐鲁软件园 </view>
+      </view>
+      <view class="sub-title"> 山东省济南市高新区六边形大楼10层</view>
+      <view class="sub-title"> 快递员:卓大 188123123123</view>
+    </view>
+    <view class="view-item">
+      <view class="title">
+        <view class="title-icon-text" type="error"> 卸 </view>
+        <view class="title-content"> 河南省郑州市郑东新区龙子湖大厦 </view>
+      </view>
+      <view class="sub-title"> 河南省郑州市郑东新区高铁路120号 </view>
+    </view>
+    <view class="view-item">
+      <view class="title">
+        <view class="title-icon-text" type="success"> 还 </view>
+        <view class="title-content"> 河南省洛阳市洛龙区白马寺 (胡克) </view>
+      </view>
+      <view class="sub-title"> 河南省洛阳市洛龙区119号 </view>
+    </view>
+  </view>
+</template>
+
+<script setup></script>
+
+<style lang="scss" scoped>
+  .line {
+    position: absolute;
+    height: 100px;
+    border-left: 1rpx dashed #444;
+    z-index: 0;
+    top: 40rpx;
+  }
+
+  .view-item {
+    margin-top: 20px;
+    position: relative;
+    z-index: 1;
+    :before {
+      content: '';
+      position: absolute;
+      left: 20rpx;
+      right: 0;
+      top: 44rpx;
+      height: 100%;
+      width: 1px;
+      background: #eeeeee;
+    }
+    .sub-title {
+      font-size: 28rpx;
+      color: #777777;
+      margin-left: 58rpx;
+      margin-top: 10px;
+    }
+    .title {
+      display: flex;
+      align-items: center;
+    }
+    .title-content {
+      font-size: 32rpx;
+      color: #444;
+      font-weight: bold;
+      padding-bottom: 5rpx;
+      flex: 1;
+    }
+    .title-icon-text {
+      width: 40rpx;
+      height: 40rpx;
+      border-radius: 12rpx;
+      text-align: center;
+      line-height: 40rpx;
+      color: #fff;
+      font-size: 26rpx;
+      margin-right: 18rpx;
+      &[type='default'] {
+        background: #1a9aff;
+      }
+      &[type='warning'] {
+        background: #f3c35b;
+      }
+      &[type='error'] {
+        background: #ff6c00;
+      }
+      &[type='success'] {
+        background: #6ec98a;
+      }
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+
+  .view-item:last-child {
+    :before {
+      content: '';
+      position: absolute;
+      left: 20rpx;
+      right: 0;
+      top: 44rpx;
+      height: 100%;
+      width: 0;
+    }
+  }
+</style>

+ 32 - 0
src/pages/order-detail/components/order-detail-base-info.vue

@@ -0,0 +1,32 @@
+<template>
+  <view class="smart-detail-card smart-margin-top60">
+    <view class="smart-detail-card-title"> 商品</view>
+    <view class="smart-detail-card-cell">
+      <view class="smart-detail-card-label"> 商品名称 </view>
+      <view class="smart-detail-card-value"> 华为MateBook 14</view>
+    </view>
+    <view class="smart-detail-card-cell">
+      <view class="smart-detail-card-label"> 价格 </view>
+      <view class="smart-detail-card-value uni-error">12,345元 </view>
+    </view>
+    <view class="smart-detail-card-cell">
+      <view class="smart-detail-card-label"> 颜色/产地 </view>
+      <view class="smart-detail-card-value">玫瑰金/中国·洛阳 </view>
+    </view>
+    <view class="smart-detail-card-cell">
+      <view class="smart-detail-card-label"> 材质/电压 </view>
+      <view class="smart-detail-card-value">金属/220V </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  const props = defineProps({
+    List: {
+      type: Array,
+      default: [],
+    },
+  });
+</script>
+
+<style lang="scss" scoped></style>

+ 102 - 0
src/pages/order-detail/components/order-detail-settle.vue

@@ -0,0 +1,102 @@
+<template>
+  <view class="smart-detail-card">
+    <view class="smart-detail-card-title"> 结算信息 </view>
+    <view class="view-item">
+      <view class="item-left"> 订单号: </view>
+      <view class="item-right"> TYD2023080979 </view>
+    </view>
+    <view class="view-item">
+      <view class="item-left"> 付款状态: </view>
+      <view class="item-right">
+        <view class="success"> 已审核 </view>
+      </view>
+    </view>
+    <view class="view-item">
+      <view class="item-left"> 应付金额: </view>
+      <view class="item-right"> 500 </view>
+    </view>
+    <view class="view-item">
+      <view class="item-left"> 已付金额: </view>
+      <view class="item-right"> 200 </view>
+    </view>
+    <view class="view-item">
+      <view class="item-left"> 收款账户: </view>
+      <view class="item-right"> 项羽/61248348910384 </view>
+    </view>
+  </view>
+</template>
+
+<script setup></script>
+
+<style lang="scss" scoped>
+  .view {
+    width: 94%;
+    background: #fff;
+    border-radius: 16rpx;
+    box-sizing: border-box;
+    padding: 0 30rpx 24rpx;
+    margin: 0 auto 24rpx;
+  }
+
+  .view-title {
+    height: 84rpx;
+    display: flex;
+    align-items: center;
+    margin-bottom: 16rpx;
+    .title-left-bg {
+      height: 28rpx;
+      width: 6rpx;
+      background: #1a9aff;
+      border-radius: 4rpx;
+      margin-right: 14rpx;
+    }
+    .title-text {
+      font-size: 32rpx;
+      color: #323333;
+      font-weight: bold;
+    }
+    .sub-title {
+      flex: 1;
+      display: flex;
+      justify-content: flex-end;
+      view {
+        height: 42rpx;
+        width: 112rpx;
+        text-align: center;
+        line-height: 42rpx;
+        font-size: 24rpx;
+        margin-left: 24rpx;
+      }
+      .success {
+        background: #e5f8e9;
+        color: #1ce36d;
+      }
+      .warning {
+        background: #fff0ed;
+        color: #ff6c00;
+      }
+    }
+  }
+
+  .view-item {
+    display: flex;
+    height: 40rpx;
+    align-items: center;
+    margin-bottom: 16rpx;
+    &:last-child {
+      margin-bottom: 0;
+    }
+    .item-left {
+      width: 180rpx;
+      margin-right: 20rpx;
+      font-size: 28rpx;
+      color: #777777;
+    }
+    .item-right {
+      font-size: 30rpx;
+      color: #323333;
+      font-weight: bold;
+      font-size: 28rpx;
+    }
+  }
+</style>

+ 52 - 0
src/pages/order-detail/order-detail.vue

@@ -0,0 +1,52 @@
+<template>
+  <view class="container">
+    <smart-detail-tabs :tabsList="tabsList" v-model="active" :fixed="true" @change="scrollTo" />
+    <view class="smart-detail">
+      <OrderDetailBaseInfo id="detail0" />
+      <DetailModelPath id="detail1" />
+      <OrderDetailSettle id="detail2" />
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import { ref } from 'vue';
+  import SmartDetailTabs from '@/components/smart-detail-tabs/index.vue';
+  import OrderDetailBaseInfo from './components/order-detail-base-info.vue';
+  import DetailModelPath from './components/detail-model-path.vue';
+  import OrderDetailSettle from './components/order-detail-settle.vue';
+
+  const tabsList = [
+    {
+      label: '基本信息',
+      value: 0,
+    },
+    {
+      label: '物流信息',
+      value: 1,
+    },
+    {
+      label: '结算信息',
+      value: 2,
+    },
+  ];
+
+  const active = ref(0);
+
+  function scrollTo(tab) {
+    uni.pageScrollTo({
+      selector: '#detail' + tab,
+      duration: 300,
+      success: console.log,
+      fail: console.log,
+    });
+  }
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    background-color: #f4f4f4;
+    height: 100vh;
+    overflow-y: scroll;
+  }
+</style>

+ 143 - 0
src/pages/select-people/select-people.vue

@@ -0,0 +1,143 @@
+<template>
+  <view>
+    <view class="select-all">
+      <view class="circle" v-if="false"> </view>
+      <image v-else src="/src/static/images/select-people/select.png" mode=""></image>
+      <view class=""> 全选 </view>
+    </view>
+    <view class="item" v-for="(item, index) in 5">
+      <view class="item-btn">
+        <view class="circle" v-if="false"> </view>
+        <image v-else src="/src/static/images/select-people/select.png" mode=""></image>
+      </view>
+      <view class="item-content">
+        <view class="item-name"> 卓大 </view>
+        <view class="item-desc"> 架构师 </view>
+      </view>
+    </view>
+    <view class="h-168"> </view>
+    <view class="footer">
+      <view class="footer-content">
+        <view class="select"> 已选择(2) </view>
+        <view class="select-item-card">
+          <view class="card-item" v-for="(item, index) in 3" :key="index"> 卓大{{ index == 2 ? '...' : '' }} </view>
+        </view>
+        <view class="submit">
+          <view class="btn"> 确认 (5/12) </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {};
+    },
+    methods: {},
+  };
+</script>
+
+<style lang="scss" scoped>
+  page {
+    background: #f5f6f8;
+  }
+  .select-all {
+    height: 104rpx;
+    width: 100%;
+    background: #fff;
+    display: flex;
+    align-items: center;
+    padding-left: 55rpx;
+    font-size: 32rpx;
+    color: #000000;
+  }
+
+  .circle {
+    width: 44rpx;
+    height: 44rpx;
+    border-radius: 50%;
+    border: 1rpx solid #ccc;
+    margin-right: 20rpx;
+  }
+  image {
+    width: 44rpx;
+    height: 44rpx;
+    margin-right: 20rpx;
+  }
+
+  .item {
+    box-sizing: border-box;
+    width: 700rpx;
+    height: 140rpx;
+    margin: 16rpx auto 0;
+    padding: 30rpx 24rpx;
+    display: flex;
+    background-color: #fff;
+    border-radius: 12rpx;
+    .item-name {
+      font-size: 32rpx;
+      font-weight: 600;
+      color: #444444;
+      margin-bottom: 8rpx;
+    }
+    .item-desc {
+      font-size: 28rpx;
+      color: #777777;
+    }
+  }
+  .h-168 {
+    height: 168rpx;
+  }
+  .footer {
+    position: fixed;
+    bottom: 0;
+    width: 100%;
+    height: 168rpx;
+    background-color: #fff;
+    padding: 16rpx 24rpx;
+    box-sizing: border-box;
+    .footer-content {
+      height: 80rpx;
+      width: 100%;
+      display: flex;
+      align-items: center;
+      .select {
+        font-size: 28rpx;
+        color: #444444;
+        margin-right: 32rpx;
+      }
+      .select-item-card {
+        display: flex;
+        view {
+          height: 44rpx;
+          padding: 0 12rpx;
+          line-height: 44rpx;
+          font-size: 22rpx;
+          color: #1a9aff;
+          border: 1px solid #2291f9;
+          border-radius: 8rpx;
+          margin-left: -12rpx;
+          position: relative;
+          background-color: #fff;
+        }
+      }
+      .submit {
+        flex: 1;
+        display: flex;
+        justify-content: flex-end;
+        view {
+          height: 80rpx;
+          width: 232rpx;
+          background: #1a9aff;
+          border-radius: 8rpx;
+          font-size: 30rpx;
+          line-height: 80rpx;
+          text-align: center;
+          color: #fff;
+        }
+      }
+    }
+  }
+</style>

+ 79 - 0
src/pages/support/change-log/change-log-detail.vue

@@ -0,0 +1,79 @@
+<template>
+  <view class="container">
+    <view class="title">
+      <uni-title type="h1" align="center" :title="detail.title"></uni-title>
+      <uni-title type="h4" align="center" color="#999999" :title="detail.subTitle"></uni-title>
+    </view>
+    <view class="content">
+      <rich-text :nodes="detail.content" />
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import { inject, reactive } from 'vue';
+  import { changeLogApi } from '@/api/support/change-log-api';
+  import { onLoad } from '@dcloudio/uni-app';
+  import { smartSentry } from '@/lib/smart-sentry';
+
+  const smartEnumPlugin = inject('smartEnumPlugin');
+
+  const detail = reactive({
+    title: '',
+    subTitle: '',
+    content: '',
+  });
+
+  async function getDetail(changeLogId) {
+    try {
+      uni.showLoading({ title: '加载中' });
+      let res = await changeLogApi.getDetail(changeLogId);
+      detail.title = res.data.version + '版本' + smartEnumPlugin.getDescByValue('CHANGE_LOG_TYPE_ENUM', res.data.type);
+      detail.content =
+        '<pre style="' +
+        'line-height: 18px;\n' +
+        'font-size: 14px;\n' +
+        'white-space: pre-wrap;\n' +
+        '  white-space: -moz-pre-wrap;\n' +
+        '  white-space: -pre-wrap;\n' +
+        '  white-space: -o-pre-wrap;\n' +
+        '  word-wrap: break-word;">' +
+        res.data.content +
+        '</pre>';
+      let subTitleArray = [];
+      if (res.data.publishAuthor) {
+        subTitleArray.push(res.data.publishAuthor);
+      }
+      if (res.data.publicDate) {
+        subTitleArray.push(res.data.publicDate);
+      }
+      detail.subTitle = subTitleArray.join(' | ');
+    } catch (e) {
+      smartSentry.captureError(e);
+    } finally {
+      uni.hideLoading();
+    }
+  }
+
+  onLoad((option) => {
+    uni.pageScrollTo({
+      scrollTop: 0,
+    });
+    getDetail(option.changeLogId);
+  });
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    height: 100vh;
+    padding: 20rpx;
+
+    .content {
+      border-top: #cccccc 1px solid;
+      margin-top: 50rpx;
+      padding: 50rpx 16rpx 50rpx 16rpx;
+      line-height: 30px;
+      color: #333333;
+    }
+  }
+</style>

+ 197 - 0
src/pages/support/change-log/change-log-list.vue

@@ -0,0 +1,197 @@
+<template>
+  <view class="container">
+    <mescroll-body @init="mescrollInit" :down="{ auto: false }" @down="onDown" @up="onUp" :up="{ toTop: { src: '' } }">
+      <!--搜索框-->
+      <uni-nav-bar :border="false" fixed :leftWidth="0" rightWidth="70px">
+        <view class="input">
+          <uni-easyinput
+            :clearable="true"
+            trim="all"
+            v-model="queryForm.keyword"
+            placeholder="搜索: 更新内容 等"
+            @confirm="search"
+            @clear="search"
+          />
+        </view>
+        <template #right>
+          <view class="nav-right" @click="search">
+            <uni-icons type="search" size="30"></uni-icons>
+            <view class="nav-right-name"> 搜索 </view>
+          </view>
+        </template>
+      </uni-nav-bar>
+
+      <!-- 列表 -->
+      <view class="list-container">
+        <view class="list-item" @click="gotoDetail(item.changeLogId)" v-for="item in listData" :key="item.changeLogId">
+          <view class="list-item-row">
+            <view class="list-item-content bolder"
+              >{{ item.version }}版本{{ $smartEnumPlugin.getDescByValue('CHANGE_LOG_TYPE_ENUM', item.type) }}</view
+            >
+            <uni-tag
+              :text="$smartEnumPlugin.getDescByValue('CHANGE_LOG_TYPE_ENUM', item.type)"
+              :type="$smartEnumPlugin.getObjectByValue('CHANGE_LOG_TYPE_ENUM', item.type).type"
+            />
+          </view>
+          <view class="list-item-row">
+            <view class="list-item-label">发布日期:{{ item.publicDate }}</view>
+          </view>
+        </view>
+      </view>
+    </mescroll-body>
+  </view>
+</template>
+
+<script setup>
+  import { reactive, ref } from 'vue';
+  import { changeLogApi } from '@/api/support/change-log-api';
+  import { onPageScroll, onReachBottom, onShow } from '@dcloudio/uni-app';
+  import useMescroll from '@/uni_modules/uni-mescroll/hooks/useMescroll';
+  import { smartSentry } from '@/lib/smart-sentry';
+  import _ from 'lodash';
+
+  // --------------------------- 查询 ---------------------------------
+
+  const defaultForm = {
+    keyword: '', //标题、内容
+    pageNum: 1,
+    pageSize: 10,
+  };
+
+  // 查询表单
+  const queryForm = reactive({ ...defaultForm });
+  // 列表数据
+  const listData = ref([]);
+
+  function buildQueryParam(pageNum) {
+    queryForm.pageNum = pageNum;
+    return Object.assign({}, queryForm, { pageNum });
+  }
+
+  async function query(mescroll, isDownFlag, param) {
+    try {
+      let res = await changeLogApi.queryPage(param);
+      if (!isDownFlag) {
+        listData.value = listData.value.concat(res.data.list);
+      } else {
+        listData.value = res.data.list;
+      }
+      mescroll.endSuccess(res.data.list.length, res.data.pages > res.data.pageNum);
+    } catch (e) {
+      smartSentry.captureError(e);
+      //联网失败, 结束加载
+      mescroll.endErr();
+    }
+  }
+
+  const { mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
+
+  /**
+   * 搜索
+   */
+  function search() {
+    query(getMescroll(), true, buildQueryParam(1));
+    uni.pageScrollTo({
+      scrollTop: 0,
+    });
+  }
+
+  /**
+   * 下拉刷新
+   */
+  function onDown(mescroll) {
+    queryForm.pageNum = 1;
+    query(mescroll, true, buildQueryParam(1));
+  }
+
+  /**
+   * 上拉加载更多
+   */
+  function onUp(mescroll) {
+    query(mescroll, false, buildQueryParam(mescroll.num));
+  }
+
+  onShow(() => {
+    search();
+  });
+
+  // --------------------------- 详情 ---------------------------------
+
+  function gotoDetail(id) {
+    uni.navigateTo({ url: '/pages/support/change-log/change-log-detail?changeLogId=' + id });
+  }
+</script>
+
+<style lang="scss" scoped>
+  .container {
+    background-color: #f4f4f4;
+  }
+  .input {
+    width: 100%;
+    height: 60rpx;
+    background: #f7f8f9;
+    border-radius: 4px;
+    margin: 8rpx 0;
+    display: flex;
+    align-items: center;
+  }
+
+  .nav-right {
+    width: 140rpx;
+    display: flex;
+    height: 88rpx;
+    flex-direction: row;
+    line-height: 88rpx;
+    .nav-right-name {
+      margin-left: 5px;
+      line-height: 88rpx;
+      font-size: 30rpx;
+    }
+  }
+
+  .list-container {
+    padding: 10rpx 20rpx;
+    margin-top: 10rpx;
+
+    .list-item {
+      background: #ffffff;
+      box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
+      margin-bottom: 20rpx;
+      padding: 30rpx 40rpx;
+
+      .list-item-row {
+        display: flex;
+        flex-direction: row;
+        margin-bottom: 16rpx;
+        justify-content: space-between;
+
+        .list-item-label {
+          font-size: 30rpx;
+          font-weight: 400;
+          text-align: left;
+          color: $uni-text-color-grey;
+        }
+        .bolder {
+          font-weight: 500 !important;
+          font-size: 34rpx !important;
+        }
+        .list-item-content {
+          font-size: 30rpx;
+          font-weight: 500;
+          text-align: left;
+        }
+        .list-item-content-container {
+          font-size: 30rpx;
+          font-weight: 500;
+          text-align: left;
+          color: #323333;
+          height: 40px;
+        }
+        .list-item-phone {
+          color: $uni-color-primary;
+          margin-left: auto;
+        }
+      }
+    }
+  }
+</style>

+ 168 - 0
src/pages/support/feedback/feedback-form.vue

@@ -0,0 +1,168 @@
+<template>
+  <view class="container">
+    <view class="smart-form">
+      <uni-forms ref="formRef" :label-width="100" :modelValue="form" label-position="left" :rules="rules">
+        <view class="smart-form-group">
+          <view class="smart-form-group-title"> 反馈内容 </view>
+          <view class="smart-form-group-content">
+            <uni-forms-item class="smart-form-item" label="意见反馈:" name="feedbackContent" required>
+              <uni-easyinput type="textarea" trim="all" v-model="form.feedbackContent" placeholder="请输入 宝贵的意见和建议" />
+            </uni-forms-item>
+            <uni-forms-item class="smart-form-item" label="相关图片:" name="unifiedSocialCreditCode">
+              <uni-file-picker
+                limit="9"
+                title="最多选择9个图片"
+                @delete="onDeleteFile"
+                v-model="feedbackFile"
+                @select="onSelectFile"
+              ></uni-file-picker>
+            </uni-forms-item>
+          </view>
+        </view>
+      </uni-forms>
+
+      <view class="smart-form-submit smart-margin-top20 bottom-button">
+        <button class="smart-form-submit-btn smart-margin-right20" type="default" @click="cancel">取消</button>
+        <button class="smart-form-submit-btn" type="primary" @click="submit">保存</button>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import { reactive, ref } from 'vue';
+  import { enterpriseApi } from '@/api/business/oa/enterprise-api';
+  import { smartSentry } from '@/lib/smart-sentry';
+  import { SmartLoading, SmartToast } from '@/lib/smart-support';
+  import { onLoad, onReady } from '@dcloudio/uni-app';
+  import { fileApi } from '@/api/support/file-api';
+  import { FILE_FOLDER_TYPE_ENUM } from '@/constants/support/file-const';
+  import _ from 'lodash';
+  import { feedbackApi } from '@/api/support/feedback-api';
+
+  // --------------------- 表单 ---------------------
+
+  const defaultFormData = {
+    feedbackContent: '',
+    feedbackAttachment: [],
+  };
+  let form = reactive({ ...defaultFormData });
+  const feedbackFile = ref([]);
+
+  const rules = {
+    feedbackContent: {
+      rules: [{ required: true, errorMessage: '请输入反馈意见' }],
+    },
+  };
+
+  // --------------------- 文件 ---------------------
+  function onSelectFile(param) {
+    let tempFilePaths = param.tempFilePaths;
+    for (const tempFilePath of tempFilePaths) {
+      upload(tempFilePath);
+    }
+
+    console.log(param, 2);
+    console.log(feedbackFile, 2);
+  }
+
+  async function upload(tempFilePath) {
+    try {
+      SmartLoading.show();
+      let res = await fileApi.upload(tempFilePath, FILE_FOLDER_TYPE_ENUM.FEEDBACK.value);
+      res.data.tempFilePath = tempFilePath;
+      form.feedbackAttachment.push(res.data);
+    } catch (e) {
+      smartSentry.captureError(e);
+    } finally {
+      SmartLoading.hide();
+    }
+  }
+
+  function onDeleteFile(param) {
+    if (!param.tempFilePath) {
+      return;
+    }
+    _.remove(form.feedbackAttachment, (e) => e.tempFilePath === param.tempFilePath);
+  }
+
+  // --------------------- 详情 ---------------------
+
+  onLoad((options) => {
+    if (options.enterpriseId) {
+      form.enterpriseId = options.enterpriseId;
+      getDetail(options.enterpriseId);
+    }
+  });
+
+  onReady(() => {
+    if (form.enterpriseId) {
+      uni.setNavigationBarTitle({
+        title: '修改客户',
+      });
+    }
+  });
+
+  async function getDetail(id) {
+    try {
+      SmartLoading.show();
+      let res = await enterpriseApi.detail(id);
+      form.enterpriseId = res.data.enterpriseId;
+      form.enterpriseName = res.data.enterpriseName;
+      form.unifiedSocialCreditCode = res.data.unifiedSocialCreditCode;
+      form.type = res.data.type;
+      form.contact = res.data.contact;
+      form.contactPhone = res.data.contactPhone;
+      form.email = res.data.email;
+      form.address = res.data.address;
+    } catch (e) {
+      smartSentry.captureError(e);
+    } finally {
+      SmartLoading.hide();
+    }
+  }
+
+  // ----------------------- 表单操作 ------------------------
+
+  const formRef = ref();
+
+  // 取消
+  function cancel() {
+    close();
+    uni.navigateBack();
+  }
+
+  // 确定
+  function submit() {
+    formRef.value
+      .validate()
+      .then(async () => {
+        SmartLoading.show();
+        try {
+          await feedbackApi.addFeedback(form);
+          SmartToast.success('提交反馈成功');
+          uni.navigateBack();
+        } catch (error) {
+          smartSentry.captureError(error);
+        } finally {
+          SmartLoading.hide();
+        }
+      })
+      .catch((error) => {
+        console.log('error', error);
+        SmartToast.toast('参数验证错误,请仔细填写表单数据!');
+      });
+  }
+</script>
+
+<style lang="scss" scoped>
+  .query-form-pop {
+    height: 800rpx;
+    overflow-y: scroll;
+  }
+
+  .bottom-button {
+    position: fixed;
+    bottom: 0;
+  }
+</style>

+ 97 - 0
src/plugins/smart-enums-plugin.js

@@ -0,0 +1,97 @@
+/*
+ * 枚举插件
+ * 此插件为 1024创新实验室 自创的插件
+ *
+ * @Author:    1024创新实验室-主任:卓大
+ * @Date:      2022-09-06 20:51:03
+ * @Wechat:    zhuda1024
+ * @Email:     lab1024@163.com
+ * @Copyright  1024创新实验室 ( https://1024lab.net ),Since 2012
+ */
+import _ from 'lodash';
+import { FLAG_NUMBER_ENUM } from '@/constants/common-const';
+
+export default {
+  install: (app, smartEnumWrapper) => {
+    const smartEnumPlugin = {};
+    /**
+     * 根据枚举值获取描述
+     * @param {*} constantName 枚举名
+     * @param {*} value          枚举值
+     * @returns
+     */
+    smartEnumPlugin.getDescByValue = function (constantName, value) {
+      if (!smartEnumWrapper || !Object.prototype.hasOwnProperty.call(smartEnumWrapper, constantName)) {
+        return '';
+      }
+      // boolean类型需要做特殊处理
+      if (constantName === 'FLAG_NUMBER_ENUM' && !_.isUndefined(value) && typeof value === 'boolean') {
+        value = value ? FLAG_NUMBER_ENUM.TRUE.value : FLAG_NUMBER_ENUM.FALSE.value;
+      }
+
+      let smartEnum = smartEnumWrapper[constantName];
+      for (let item in smartEnum) {
+        if (smartEnum[item].value === value) {
+          return smartEnum[item].desc;
+        }
+      }
+      return '';
+    };
+    /**
+     * 根据枚举值获取对象
+     * @param {*} constantName 枚举名
+     * @param {*} value          枚举值
+     * @returns
+     */
+    smartEnumPlugin.getObjectByValue = function (constantName, value) {
+      if (!smartEnumWrapper || !Object.prototype.hasOwnProperty.call(smartEnumWrapper, constantName)) {
+        return '';
+      }
+
+      let smartEnum = smartEnumWrapper[constantName];
+      for (let item in smartEnum) {
+        if (smartEnum[item].value === value) {
+          return smartEnum[item];
+        }
+      }
+      return null;
+    };
+    /**
+     * 根据枚举名获取对应的描述键值对[{value:desc}]
+     * @param {*} constantName 枚举名
+     * @returns
+     */
+    smartEnumPlugin.getValueDescList = function (constantName) {
+      if (!Object.prototype.hasOwnProperty.call(smartEnumWrapper, constantName)) {
+        return [];
+      }
+      const result = [];
+      let targetSmartEnum = smartEnumWrapper[constantName];
+      for (let item in targetSmartEnum) {
+        result.push(targetSmartEnum[item]);
+      }
+      return result;
+    };
+
+    /**
+     * 根据枚举名获取对应的value描述键值对{value:desc}
+     * @param {*} constantName 枚举名
+     * @returns
+     */
+    smartEnumPlugin.getValueDesc = function (constantName) {
+      if (!Object.prototype.hasOwnProperty.call(smartEnumWrapper, constantName)) {
+        return {};
+      }
+      let smartEnum = smartEnumWrapper[constantName];
+      let result = {};
+      for (let item in smartEnum) {
+        let key = smartEnum[item].value + '';
+        result[key] = smartEnum[item].desc;
+      }
+      return result;
+    };
+
+    app.config.globalProperties.$smartEnumPlugin = smartEnumPlugin;
+    app.provide('smartEnumPlugin', smartEnumPlugin);
+  },
+};

+ 6 - 0
src/shime-uni.d.ts

@@ -0,0 +1,6 @@
+export {};
+
+declare module "vue" {
+  type Hooks = App.AppInstance & Page.PageInstance;
+  interface ComponentCustomOptions extends Hooks {}
+}

BIN
src/static/common/back-icon.png


BIN
src/static/common/update-app.png


BIN
src/static/images/form/add-image.png


BIN
src/static/images/form/add.png


BIN
src/static/images/form/back.png


BIN
src/static/images/form/close-image.png


BIN
src/static/images/form/submit.png


BIN
src/static/images/form/title-bg.png


BIN
src/static/images/home/admin-icon.png


BIN
src/static/images/home/copy-icon.png


Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff