陈鹏铭 před 2 roky
revize
b4fe02b40b
100 změnil soubory, kde provedl 48357 přidání a 0 odebrání
  1. 21 0
      LICENSE
  2. 76 0
      README-zh.md
  3. 76 0
      README.md
  4. 14 0
      babel.config.js
  5. 35 0
      build/index.js
  6. 24 0
      jest.config.js
  7. 66 0
      mock/index.js
  8. 68 0
      mock/mock-server.js
  9. 29 0
      mock/table.js
  10. 85 0
      mock/user.js
  11. 0 0
      output.js
  12. 22956 0
      package-lock.json
  13. 83 0
      package.json
  14. 8 0
      postcss.config.js
  15. 15 0
      prettier.config.js
  16. binární
      public/base.ico
  17. binární
      public/excel/税收导入模板.xlsx
  18. binární
      public/excel/营收导入模板.xlsx
  19. binární
      public/favicon.ico
  20. 44 0
      public/index.html
  21. 9 0
      public/libs/axios.min.js
  22. 1 0
      public/libs/index.js
  23. 1 0
      public/libs/qs.min.js
  24. 6 0
      public/libs/vue-router.min.js
  25. 6 0
      public/libs/vue.min.js
  26. 21703 0
      public/libs/xlsx.js
  27. binární
      public/logo.png
  28. 22 0
      public/mobile.html
  29. 5 0
      src.mobile/App.vue
  30. 57 0
      src.mobile/api/apis.js
  31. binární
      src.mobile/assets/mobile-Page-Image/454.png
  32. binární
      src.mobile/assets/mobile-Page-Image/bg01.png
  33. binární
      src.mobile/assets/mobile-Page-Image/bg02.png
  34. binární
      src.mobile/assets/mobile-Page-Image/home_slices.zip
  35. binární
      src.mobile/assets/mobile-Page-Image/home_slices/127.png
  36. binární
      src.mobile/assets/mobile-Page-Image/home_slices/134.png
  37. binární
      src.mobile/assets/mobile-Page-Image/home_slices/mbz2.png
  38. binární
      src.mobile/assets/mobile-Page-Image/home_slices/z_127.png
  39. binární
      src.mobile/assets/mobile-Page-Image/home_slices/z_134.png
  40. binární
      src.mobile/assets/mobile-Page-Image/home_slices/z_459.png
  41. binární
      src.mobile/assets/mobile-Page-Image/home_slices/z_460.png
  42. binární
      src.mobile/assets/mobile-Page-Image/jx_186.png
  43. binární
      src.mobile/assets/mobile-Page-Image/right2x.png
  44. binární
      src.mobile/assets/mobile-Page-Image/z_454.png
  45. binární
      src.mobile/assets/mobile-Page-Image/z_461.png
  46. binární
      src.mobile/assets/mobile-Page-Image/z_462.png
  47. 24 0
      src.mobile/main.js
  48. 182 0
      src.mobile/plugin/axios/common.js
  49. 120 0
      src.mobile/plugin/axios/constant.js
  50. 53 0
      src.mobile/plugin/axios/index.js
  51. 44 0
      src.mobile/plugin/axios/util.cookies.js
  52. 25 0
      src.mobile/plugin/axios/util.js
  53. 146 0
      src.mobile/router/index.js
  54. binární
      src.mobile/static/img.jpg
  55. 10 0
      src.mobile/store/getters.js
  56. 25 0
      src.mobile/store/index.js
  57. 57 0
      src.mobile/store/modules/layout.js
  58. 149 0
      src.mobile/store/modules/user.js
  59. 62 0
      src.mobile/vant.js
  60. 229 0
      src.mobile/views/FireTable.vue
  61. 66 0
      src.mobile/views/index.vue
  62. 150 0
      src.mobile/views/login.vue
  63. 22 0
      src.mobile/views/logout.vue
  64. 234 0
      src.mobile/views/mobile-page/components/data-filling.vue
  65. 153 0
      src.mobile/views/mobile-page/components/historical-filling.vue
  66. 107 0
      src.mobile/views/mobile-page/mobile-changePsw.vue
  67. 40 0
      src.mobile/views/mobile-page/mobile-estimatedData.vue
  68. 119 0
      src.mobile/views/mobile-page/mobile-home.vue
  69. 178 0
      src.mobile/views/mobile-page/mobile-login.vue
  70. 67 0
      src.mobile/views/mobile-page/mobile-main.vue
  71. 59 0
      src.mobile/views/mobile-page/mobile-myInfor.vue
  72. 43 0
      src.mobile/views/report.vue
  73. 34 0
      src.mobile/views/reportDetail.vue
  74. 163 0
      src.mobile/views/reportMain.vue
  75. 323 0
      src.mobile/views/sign.vue
  76. 11 0
      src/App.vue
  77. 52 0
      src/api/user.js
  78. binární
      src/assets/404_images/404.png
  79. binární
      src/assets/404_images/404_cloud.png
  80. binární
      src/assets/mobile-Page-Image/454.png
  81. binární
      src/assets/mobile-Page-Image/bg01.png
  82. binární
      src/assets/mobile-Page-Image/bg02.png
  83. binární
      src/assets/mobile-Page-Image/right2x.png
  84. binární
      src/assets/mobile-Page-Image/矩形 186.png
  85. binární
      src/assets/mobile-Page-Image/组 454.png
  86. binární
      src/assets/mobile-Page-Image/组 461.png
  87. binární
      src/assets/mobile-Page-Image/组 462.png
  88. binární
      src/assets/mobile-Page-Image/首页_slices/127.png
  89. binární
      src/assets/mobile-Page-Image/首页_slices/134.png
  90. binární
      src/assets/mobile-Page-Image/首页_slices/ddc79472428947.5be942464d407@2x.png
  91. binární
      src/assets/mobile-Page-Image/首页_slices/组 127.png
  92. binární
      src/assets/mobile-Page-Image/首页_slices/组 134.png
  93. binární
      src/assets/mobile-Page-Image/首页_slices/组 459.png
  94. binární
      src/assets/mobile-Page-Image/首页_slices/组 460.png
  95. binární
      src/assets/mobile-Page-Image/首页_slices/蒙版组 2.png
  96. binární
      src/assets/newLogin-image/Desktop/组 464.png
  97. binární
      src/assets/newLogin-image/Desktop/组 466.png
  98. binární
      src/assets/newLogin-image/Desktop/组 467.png
  99. binární
      src/assets/newLogin-image/QR2X.png
  100. 0 0
      src/assets/newLogin-image/qrcode.png

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017-present PanJiaChen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 76 - 0
README-zh.md

@@ -0,0 +1,76 @@
+# Rock-Star 1.0 页面版
+
+> 这是一个Rock-Star系统体系的基础平台后台管理。它包含了 Element UI & axios & iconfont & permission control & lint,这些搭建后台必要的东西。
+
+目前版本为 `v4.0+` 基于 `vue-cli` 进行构建
+整个项目是基于panjiachen的vue-element-admin为基础进行构建的 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
+
+## Extra
+
+如果你想要根据用户角色来动态生成侧边栏和 router,你可以使用该分支[permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control)
+
+## Build Setup
+
+```bash
+# 克隆项目
+git clone http://47.96.128.196:3000/pengyq/rock-web-base.git
+
+# 进入项目目录
+cd rock-web-base
+
+# 安装依赖
+npm install
+
+# 建议不要直接使用 cnpm 安装以来,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
+npm install --registry=https://registry.npm.taobao.org
+
+# 启动服务
+npm run dev
+```
+
+浏览器访问 [http://localhost:9525](http://localhost:9525)
+
+## 发布
+
+```bash
+# 构建测试环境
+npm run build:stage
+
+# 构建生产环境
+npm run build:prod
+
+# 本地运行代码
+npm run dev
+```
+
+## 其它
+
+```bash
+# 预览发布环境效果
+npm run preview
+
+# 预览发布环境效果 + 静态资源分析
+npm run preview -- --report
+
+# 代码格式检查
+npm run lint
+
+# 代码格式检查并自动修复
+npm run lint -- --fix
+```
+
+更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
+
+## Browsers support
+
+Modern browsers and Internet Explorer 10+.
+
+| IE / Edge |   Firefox |    Chrome |   Safari  |
+| --------- | --------- | --------- | --------- |
+| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions |
+
+## License
+
+PanJiaChen [MIT](https://github.com/PanJiaChen/vue-admin-template/blob/master/LICENSE) license.
+
+Copyright (c) 2020-present Rock-Idea

+ 76 - 0
README.md

@@ -0,0 +1,76 @@
+# Rock-Star 1.0 页面版
+
+> 这是一个Rock-Star系统体系的基础平台后台管理。它包含了 Element UI & axios & iconfont & permission control & lint,这些搭建后台必要的东西。
+
+目前版本为 `v4.0+` 基于 `vue-cli` 进行构建
+整个项目是基于panjiachen的vue-element-admin为基础进行构建的 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
+
+## Extra
+
+如果你想要根据用户角色来动态生成侧边栏和 router,你可以使用该分支[permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control)
+
+## Build Setup
+
+```bash
+# 克隆项目
+git clone http://47.96.128.196:3000/pengyq/rock-web-base.git
+
+# 进入项目目录
+cd rock-web-base
+
+# 安装依赖
+npm install
+
+# 建议不要直接使用 cnpm 安装以来,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
+npm install --registry=https://registry.npm.taobao.org
+
+# 启动服务
+npm run dev
+```
+
+浏览器访问 [http://localhost:9525](http://localhost:9525)
+
+## 发布
+
+```bash
+# 构建测试环境
+npm run build:stage
+
+# 构建生产环境
+npm run build:prod
+
+# 本地运行代码
+npm run dev
+```
+
+## 其它
+
+```bash
+# 预览发布环境效果
+npm run preview
+
+# 预览发布环境效果 + 静态资源分析
+npm run preview -- --report
+
+# 代码格式检查
+npm run lint
+
+# 代码格式检查并自动修复
+npm run lint -- --fix
+```
+
+更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
+
+## Browsers support
+
+Modern browsers and Internet Explorer 10+.
+
+| IE / Edge |   Firefox |    Chrome |   Safari  |
+| --------- | --------- | --------- | --------- |
+| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions |
+
+## License
+
+PanJiaChen [MIT](https://github.com/PanJiaChen/vue-admin-template/blob/master/LICENSE) license.
+
+Copyright (c) 2020-present Rock-Idea

+ 14 - 0
babel.config.js

@@ -0,0 +1,14 @@
+module.exports = {
+    presets: ['@vue/app'],
+    plugins: [
+        [
+            'import',
+            {
+                libraryName: 'vant',
+                libraryDirectory: 'es',
+                style: true
+            },
+            'vant'
+        ]
+    ]
+}

+ 35 - 0
build/index.js

@@ -0,0 +1,35 @@
+const { run } = require('runjs')
+const chalk = require('chalk')
+const config = require('../vue.config.js')
+const rawArgv = process.argv.slice(2)
+const args = rawArgv.join(' ')
+
+if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
+  const report = rawArgv.includes('--report')
+
+  run(`vue-cli-service build ${args}`)
+
+  const port = 9526
+  const publicPath = config.publicPath
+
+  var connect = require('connect')
+  var serveStatic = require('serve-static')
+  const app = connect()
+
+  app.use(
+    publicPath,
+    serveStatic('./dist', {
+      index: ['index.html', '/']
+    })
+  )
+
+  app.listen(port, function () {
+    console.log(chalk.green(`> Preview at  http://localhost:${port}${publicPath}`))
+    if (report) {
+      console.log(chalk.green(`> Report at  http://localhost:${port}${publicPath}report.html`))
+    }
+
+  })
+} else {
+  run(`vue-cli-service build ${args}`)
+}

+ 24 - 0
jest.config.js

@@ -0,0 +1,24 @@
+module.exports = {
+    moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
+    transform: {
+        '^.+\\.vue$': 'vue-jest',
+        '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
+      'jest-transform-stub',
+        '^.+\\.jsx?$': 'babel-jest'
+    },
+    moduleNameMapper: {
+        '^@/(.*)$': '<rootDir>/src/$1'
+    },
+    snapshotSerializers: ['jest-serializer-vue'],
+    testMatch: [
+        '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
+    ],
+    collectCoverageFrom: ['src/static/utils/**/*.{js,vue}', '!src/static/utils/auth.js', 'src/components/**/*.{js,vue}'],
+    coverageDirectory: '<rootDir>/tests/unit/coverage',
+    // 'collectCoverage': true,
+    'coverageReporters': [
+        'lcov',
+        'text-summary'
+    ],
+    testURL: 'http://localhost/'
+}

+ 66 - 0
mock/index.js

@@ -0,0 +1,66 @@
+import Mock from 'mockjs'
+import { param2Obj } from '../src/static/utils'
+
+import user from './user'
+import table from './table'
+
+const mocks = [
+    ...user,
+    ...table
+]
+
+// for front mock
+// please use it cautiously, it will redefine XMLHttpRequest,
+// which will cause many of your third-party libraries to be invalidated(like progress event).
+export function mockXHR() {
+    // mock patch
+    // https://github.com/nuysoft/Mock/issues/300
+    Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
+    Mock.XHR.prototype.send = function() {
+        if (this.custom.xhr) {
+            this.custom.xhr.withCredentials = this.withCredentials || false
+
+            if (this.responseType) {
+                this.custom.xhr.responseType = this.responseType
+            }
+        }
+        this.proxy_send(...arguments)
+    }
+
+    function XHR2ExpressReqWrap(respond) {
+        return function(options) {
+            let result = null
+            if (respond instanceof Function) {
+                const { body, type, url } = options
+                // https://expressjs.com/en/4x/api.html#req
+                result = respond({
+                    method: type,
+                    body: JSON.parse(body),
+                    query: param2Obj(url)
+                })
+            } else {
+                result = respond
+            }
+            return Mock.mock(result)
+        }
+    }
+
+    for (const i of mocks) {
+        Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response))
+    }
+}
+
+// for mock server
+const responseFake = (url, type, respond) => {
+    return {
+        url: new RegExp(`/mock${url}`),
+        type: type || 'get',
+        response(req, res) {
+            res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
+        }
+    }
+}
+
+export default mocks.map(route => {
+    return responseFake(route.url, route.type, route.response)
+})

+ 68 - 0
mock/mock-server.js

@@ -0,0 +1,68 @@
+const chokidar = require('chokidar')
+const bodyParser = require('body-parser')
+const chalk = require('chalk')
+const path = require('path')
+
+const mockDir = path.join(process.cwd(), 'mock')
+
+function registerRoutes(app) {
+    let mockLastIndex
+    const { default: mocks } = require('./index.js')
+    for (const mock of mocks) {
+        app[mock.type](mock.url, mock.response)
+        mockLastIndex = app._router.stack.length
+    }
+    const mockRoutesLength = Object.keys(mocks).length
+    return {
+        mockRoutesLength: mockRoutesLength,
+        mockStartIndex: mockLastIndex - mockRoutesLength
+    }
+}
+
+function unregisterRoutes() {
+    Object.keys(require.cache).forEach(i => {
+        if (i.includes(mockDir)) {
+            delete require.cache[require.resolve(i)]
+        }
+    })
+}
+
+module.exports = app => {
+    // es6 polyfill
+    require('@babel/register')
+
+    // parse app.body
+    // https://expressjs.com/en/4x/api.html#req.body
+    app.use(bodyParser.json())
+    app.use(bodyParser.urlencoded({
+        extended: true
+    }))
+
+    const mockRoutes = registerRoutes(app)
+    var mockRoutesLength = mockRoutes.mockRoutesLength
+    var mockStartIndex = mockRoutes.mockStartIndex
+
+    // watch files, hot reload mock server
+    chokidar.watch(mockDir, {
+        ignored: /mock-server/,
+        ignoreInitial: true
+    }).on('all', (event, path) => {
+        if (event === 'change' || event === 'add') {
+            try {
+                // remove mock routes stack
+                app._router.stack.splice(mockStartIndex, mockRoutesLength)
+
+                // clear routes cache
+                unregisterRoutes()
+
+                const mockRoutes = registerRoutes(app)
+                mockRoutesLength = mockRoutes.mockRoutesLength
+                mockStartIndex = mockRoutes.mockStartIndex
+
+                console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed  ${path}`))
+            } catch (error) {
+                console.log(chalk.redBright(error))
+            }
+        }
+    })
+}

+ 29 - 0
mock/table.js

@@ -0,0 +1,29 @@
+import Mock from 'mockjs'
+
+const data = Mock.mock({
+    'items|30': [{
+        id: '@id',
+        title: '@sentence(10, 20)',
+        'status|1': ['published', 'draft', 'deleted'],
+        author: 'name',
+        display_time: '@datetime',
+        pageviews: '@integer(300, 5000)'
+    }]
+})
+
+export default [
+    {
+        url: '/table/list',
+        type: 'get',
+        response: config => {
+            const items = data.items
+            return {
+                code: 20000,
+                data: {
+                    total: items.length,
+                    items: items
+                }
+            }
+        }
+    }
+]

+ 85 - 0
mock/user.js

@@ -0,0 +1,85 @@
+
+const tokens = {
+    admin: {
+        token: 'admin-token'
+    },
+    editor: {
+        token: 'editor-token'
+    }
+}
+
+const users = {
+    'admin-token': {
+        roles: ['admin'],
+        introduction: 'I am a super administrator',
+        avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
+        name: 'Super Admin'
+    },
+    'editor-token': {
+        roles: ['editor'],
+        introduction: 'I am an editor',
+        avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
+        name: 'Normal Editor'
+    }
+}
+
+export default [
+    // user login
+    {
+        url: '/user/login',
+        type: 'post',
+        response: config => {
+            console.log('mock/user/login')
+            const { username } = config.body
+            const token = tokens[username]
+
+            // mock error
+            if (!token) {
+                return {
+                    code: 60204,
+                    message: 'Account and password are incorrect.'
+                }
+            }
+
+            return {
+                code: 20000,
+                data: token
+            }
+        }
+    },
+
+    // get user info
+    {
+        url: '/user/info\.*',
+        type: 'get',
+        response: config => {
+            const { token } = config.query
+            const info = users[token]
+
+            // mock error
+            if (!info) {
+                return {
+                    code: 50008,
+                    message: 'Login failed, unable to get user details.'
+                }
+            }
+
+            return {
+                code: 20000,
+                data: info
+            }
+        }
+    },
+
+    // user logout
+    {
+        url: '/user/logout',
+        type: 'post',
+        response: _ => {
+            return {
+                code: 20000,
+                data: 'success'
+            }
+        }
+    }
+]

+ 0 - 0
output.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 22956 - 0
package-lock.json


+ 83 - 0
package.json

@@ -0,0 +1,83 @@
+{
+  "name": "rock_web_base",
+  "version": "1.4.0",
+  "description": "Idea base template based vue-admin-template",
+  "author": "Peng <peng.y.q@hotmail.com>",
+  "license": "MIT",
+  "scripts": {
+    "dev": "vue-cli-service serve",
+    "build:prod": "vue-cli-service build",
+    "build:stage": "vue-cli-service build --mode staging",
+    "preview": "node build/index.js --preview",
+    "lint": "eslint --ext .js,.vue src",
+    "test:unit": "jest --clearCache && vue-cli-service test:unit",
+    "test:ci": "npm run lint && npm run test:unit",
+    "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
+  },
+  "dependencies": {
+    "axios": "^0.26.1",
+    "babel-eslint": "^8.2.2",
+    "echarts": "^4.2.1",
+    "element-ui": "2.10.1",
+    "flex.css": "^1.1.7",
+    "gg-editor": "^3.0.0-beta.3",
+    "js-cookie": "2.2.0",
+    "js-md5": "^0.7.3",
+    "lodash": "^4.17.20",
+    "normalize.css": "7.0.0",
+    "nprogress": "0.2.0",
+    "path-to-regexp": "2.4.0",
+    "qs.js": "^0.1.12",
+    "quill-image-extend-module": "^1.1.2",
+    "quill-image-resize-module": "^3.0.0",
+    "react": "^16.11.0",
+    "react-dom": "^16.13.0",
+    "screenfull": "^4.2.1",
+    "v-viewer": "^1.6.4",
+    "vant": "^2.12.48",
+    "vue": "2.6.10",
+    "vue-count-to": "^1.0.13",
+    "vue-quill-editor": "^3.0.6",
+    "vue-router": "3.0.6",
+    "vuex": "3.1.0",
+    "xlsx": "^0.18.5"
+  },
+  "devDependencies": {
+    "@babel/core": "7.0.0",
+    "@babel/register": "7.0.0",
+    "@vue/cli-plugin-babel": "3.6.0",
+    "@vue/cli-plugin-eslint": "3.6.0",
+    "@vue/cli-plugin-unit-jest": "^3.8.0",
+    "@vue/cli-service": "3.6.0",
+    "@vue/test-utils": "1.0.0-beta.29",
+    "autoprefixer": "^9.5.1",
+    "babel-core": "7.0.0-bridge.0",
+    "babel-jest": "23.6.0",
+    "babel-plugin-import": "^1.13.5",
+    "chalk": "2.4.2",
+    "compression-webpack-plugin": "^3.0.1",
+    "connect": "3.6.6",
+    "eslint": "5.15.3",
+    "eslint-plugin-vue": "5.2.2",
+    "html-webpack-plugin": "3.2.0",
+    "mockjs": "1.0.1-beta3",
+    "prettier": "^2.6.2",
+    "runjs": "^4.3.2",
+    "sass": "^1.53.0",
+    "sass-loader": "^7.1.0",
+    "script-ext-html-webpack-plugin": "2.1.3",
+    "script-loader": "0.7.2",
+    "serve-static": "^1.13.2",
+    "svg-sprite-loader": "4.1.3",
+    "svgo": "1.2.2",
+    "vue-template-compiler": "2.6.10"
+  },
+  "engines": {
+    "node": ">=8.9",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions"
+  ]
+}

+ 8 - 0
postcss.config.js

@@ -0,0 +1,8 @@
+// https://github.com/michael-ciniawsky/postcss-load-config
+
+module.exports = {
+    'plugins': {
+    // to edit target browsers: use "browserslist" field in package.json
+        'autoprefixer': {}
+    }
+}

+ 15 - 0
prettier.config.js

@@ -0,0 +1,15 @@
+// prettier.config.js
+module.exports = {
+    printWidth: 100, // 每行代码长度(默认80)
+    tabWidth: 4, // 每个tab相当于多少个空格(默认2)ab进行缩进(默认false)
+    useTabs: false, // 是否使用t
+    singleQuote: true, // 使用单引号(默认false)
+    semi: false, // 声明结尾使用分号(默认true)
+    trailingComma: 'none', // 多行使用拖尾逗号(默认none)all
+    bracketSpacing: true, // 对象字面量的大括号间使用空格(默认true)
+    jsxBracketSameLine: false, // 多行JSX中的>放置在最后一行的结尾,而不是另起一行(默认false)
+    // 箭头函数参数括号 默认avoid 可选 avoid| always
+    // avoid 能省略括号的时候就省略 例如x => x
+    // always 总是有括号
+    arrowParens: 'avoid'
+}

binární
public/base.ico


binární
public/excel/税收导入模板.xlsx


binární
public/excel/营收导入模板.xlsx


binární
public/favicon.ico


+ 44 - 0
public/index.html

@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+    <link rel="icon" href="<%= BASE_URL %>base.ico">
+    <title><%= webpackConfig.name %></title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!--<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>-->
+    <!--<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.0.6/vue-router.min.js"></script>-->
+    <!--<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>-->
+    <!--<script src="https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.10.1/index.js"></script>-->
+    <!--<script src="https://cdnjs.cloudflare.com/ajax/libs/qs/6.5.2/qs.min.js"></script>-->
+
+    <!--<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>-->
+    <!--<script src="https://cdn.bootcss.com/vue-router/3.0.6/vue-router.min.js"></script>-->
+    <!--<script src="https://cdn.bootcss.com/axios/0.19.0/axios.min.js"></script>-->
+    <!--<script src="https://cdn.bootcss.com/element-ui/2.10.1/index.js"></script>-->
+    <!--<script src="https://cdn.bootcss.com/qs/6.5.2/qs.min.js"></script>-->
+
+<!--    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.10/vue.min.js"></script>
+    <script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.0.6/vue-router.min.js"></script>
+    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.0/axios.min.js"></script>
+    <script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.2/index.js"></script>
+    <script src="https://cdn.bootcdn.net/ajax/libs/qs/6.5.2/qs.min.js"></script>
+
+    <script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.16.8/xlsx.js"></script> -->
+
+    <script src="<%= BASE_URL %>libs/vue.min.js"></script>
+    <script src="<%= BASE_URL %>libs/vue-router.min.js"></script>
+    <script src="<%= BASE_URL %>libs/axios.min.js"></script>
+    <script src="<%= BASE_URL %>libs/index.js"></script>
+    <script src="<%= BASE_URL %>libs/qs.min.js"></script>
+
+    <script src="<%= BASE_URL %>libs/xlsx.js"></script>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 9 - 0
public/libs/axios.min.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
public/libs/index.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
public/libs/qs.min.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 6 - 0
public/libs/vue-router.min.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 6 - 0
public/libs/vue.min.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 21703 - 0
public/libs/xlsx.js


binární
public/logo.png


+ 22 - 0
public/mobile.html

@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+    <link rel="icon" href="<%= BASE_URL %>base.ico">
+    <title><%= webpackConfig.name %></title>
+</head>
+<body>
+<noscript>
+    <strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+</noscript>
+<div id="app"></div>
+<script src="<%= BASE_URL %>libs/vue.min.js"></script>
+<script src="<%= BASE_URL %>libs/vue-router.min.js"></script>
+<script src="<%= BASE_URL %>libs/axios.min.js"></script>
+<script src="<%= BASE_URL %>libs/index.js"></script>
+<script src="<%= BASE_URL %>libs/qs.min.js"></script>
+<script src="<%= BASE_URL %>libs/xlsx.js"></script>
+</body>
+</html>

+ 5 - 0
src.mobile/App.vue

@@ -0,0 +1,5 @@
+<template>
+    <div id="app" style="background-color: #eee">
+        <router-view />
+    </div>
+</template>

+ 57 - 0
src.mobile/api/apis.js

@@ -0,0 +1,57 @@
+import request from '@.mobile/plugin/axios'
+import qs from 'qs'
+
+function getHeaders() {
+    return {
+        'MVVM-Key': String(new Date().getTime()),
+        xx: 'anything'
+    }
+}
+
+const all = {
+    getUser: function (id) {
+        return request({
+            headers: getHeaders(),
+            url: '/user/' + id,
+            method: 'get',
+            loading: {
+                type: 'nprogress',
+                interval: 500
+            }
+        })
+    },
+    login: function (ctrl, url, postData) {
+        return request({
+            headers: getHeaders(),
+            url: '/' + ctrl + '/' + url,
+            method: 'post',
+            data: qs.stringify(postData)
+        })
+    },
+    logout: function (ctrl, url, postData) {
+        return request({
+            headers: getHeaders(),
+            url: '/' + ctrl + '/' + url,
+            method: 'post',
+            data: qs.stringify(postData)
+        })
+    },
+    requestBase: function (url, postData) {
+        return request({
+            headers: getHeaders(),
+            url: '/FireH5Controller/' + url,
+            method: 'post',
+            data: qs.stringify(postData)
+        })
+    },
+    requestData: function (ctrl, url, postData) {
+        return request({
+            headers: getHeaders(),
+            url: '/' + ctrl + '/' + url,
+            method: 'post',
+            data: qs.stringify(postData)
+        })
+    }
+}
+
+export default all

binární
src.mobile/assets/mobile-Page-Image/454.png


binární
src.mobile/assets/mobile-Page-Image/bg01.png


binární
src.mobile/assets/mobile-Page-Image/bg02.png


binární
src.mobile/assets/mobile-Page-Image/home_slices.zip


binární
src.mobile/assets/mobile-Page-Image/home_slices/127.png


binární
src.mobile/assets/mobile-Page-Image/home_slices/134.png


binární
src.mobile/assets/mobile-Page-Image/home_slices/mbz2.png


binární
src.mobile/assets/mobile-Page-Image/home_slices/z_127.png


binární
src.mobile/assets/mobile-Page-Image/home_slices/z_134.png


binární
src.mobile/assets/mobile-Page-Image/home_slices/z_459.png


binární
src.mobile/assets/mobile-Page-Image/home_slices/z_460.png


binární
src.mobile/assets/mobile-Page-Image/jx_186.png


binární
src.mobile/assets/mobile-Page-Image/right2x.png


binární
src.mobile/assets/mobile-Page-Image/z_454.png


binární
src.mobile/assets/mobile-Page-Image/z_461.png


binární
src.mobile/assets/mobile-Page-Image/z_462.png


+ 24 - 0
src.mobile/main.js

@@ -0,0 +1,24 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import store from './store'
+import apis from './api/apis'
+import common from './plugin/axios/common'
+import constant from './plugin/axios/constant'
+
+import 'flex.css'
+import './vant'
+
+const Qs = require('qs')
+Vue.prototype.$qs = Qs
+Vue.prototype.$apis = apis
+Vue.prototype.$common = common
+Vue.prototype.$constant = constant
+
+Vue.config.productionTip = false
+
+new Vue({
+    router,
+    store,
+    render: h => h(App)
+}).$mount('#app')

+ 182 - 0
src.mobile/plugin/axios/common.js

@@ -0,0 +1,182 @@
+import constant from './constant'
+
+const vKey = location.host
+
+export default {
+    hashCode: function (str) {
+        let hash = 0
+        if (str.length === 0) return hash
+        for (let i = 0; i < str.length; i++) {
+            const char = str.charCodeAt(i)
+            hash = (hash << 5) - hash + char
+            hash = hash & hash // Convert to 32bit integer
+        }
+        return hash
+    },
+    uuid: function (len, radix) {
+        var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
+        var uuid = []
+        var i
+        radix = radix || chars.length
+
+        if (len) {
+            // Compact form
+            for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)]
+        } else {
+            // rfc4122, version 4 form
+            var r
+
+            // rfc4122 requires these characters
+            uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
+            uuid[14] = '4'
+
+            // Fill in random data.  At i==19 set the high bits of clock sequence as
+            // per rfc4122, sec. 4.1.5
+            for (i = 0; i < 36; i++) {
+                if (!uuid[i]) {
+                    r = 0 | (Math.random() * 16)
+                    uuid[i] = chars[i === 19 ? (r & 0x3) | 0x8 : r]
+                }
+            }
+        }
+
+        return uuid.join('')
+    },
+    setUser: function (value) {
+        localStorage.setItem(this.UserKey(), JSON.stringify(value))
+    },
+
+    currUser: function () {
+        const userKey = localStorage.getItem(this.UserKey())
+        if (userKey) {
+            const user = JSON.parse(userKey || '')
+            // console.log('curr user:', user)
+            if (user) {
+                return user
+            }
+        }
+
+        // this.logout()
+        return null
+    },
+
+    uid: function () {
+        return this.currUser() ? this.currUser().id : null
+    },
+
+    isAdmin: function () {
+        return this.uid() === '1'
+    },
+
+    setBiz: function (value) {
+        localStorage.setItem(this.BizKey(), JSON.stringify(value))
+    },
+
+    currBiz: function () {
+        return JSON.parse(localStorage.getItem(this.BizKey()) || null)
+    },
+
+    bid: function () {
+        return this.currBiz() ? this.currBiz().id : null
+    },
+
+    setUserType: function (value) {
+        localStorage.setItem(vKey + '_' + constant.KEY_USER_TYPE, value)
+    },
+
+    currUserType: function () {
+        return localStorage.getItem(vKey + '_' + constant.KEY_USER_TYPE) || '1'
+    },
+
+    setCode: function (value) {
+        localStorage.setItem(constant.KEY_CODE, value)
+    },
+
+    currCode: function () {
+        return localStorage.getItem(constant.KEY_CODE) || null
+    },
+
+    setMenu: function (value) {
+        localStorage.setItem(this.MenuKey(), JSON.stringify(value))
+    },
+
+    currMenu: function () {
+        const menuVal = this.getMenuVal()
+        if (menuVal && menuVal !== 'undefined') {
+            return JSON.parse(menuVal)
+        } else {
+            return []
+        }
+    },
+
+    getMenuVal: function () {
+        return localStorage.getItem(this.MenuKey())
+    },
+
+    removeStorage: function (_key) {
+        localStorage.removeItem(_key)
+    },
+
+    // ================================ Common Key ==============================================
+
+    // UserKey: function() {
+    //     return window.location.host + '_' + constant.KEY_USER + this.currUserType()
+    // },
+    //
+    // MenuKey: function() {
+    //     return window.location.host + '_' + constant.KEY_USER + this.currUserType() + '_' + constant.KEY_USER_MENU
+    // },
+
+    UserKey: function () {
+        return this.hashCode(window.location.host + '_' + constant.KEY_USER + this.currUserType())
+    },
+
+    BizKey: function () {
+        return this.hashCode(window.location.host + '_' + constant.KEY_BIZ + this.currUserType())
+    },
+
+    MenuKey: function () {
+        return this.hashCode(
+            window.location.host +
+                '_' +
+                constant.KEY_USER +
+                this.currUserType() +
+                '_' +
+                constant.KEY_USER_MENU
+        )
+    },
+
+    replaceFileUrl: function (urlPath) {
+        if (urlPath != null && urlPath !== '') {
+            urlPath = urlPath.replaceAll(
+                '/server/FileController/download/',
+                constant.BASE_URI + '/FileController/download/'
+            )
+        }
+        return urlPath
+    },
+    formatDate: function (fmt, date) {
+        // author: meizz
+        var o = {
+            'M+': date.getMonth() + 1, // 月份
+            'd+': date.getDate(), // 日
+            'h+': date.getHours(), // 小时
+            'm+': date.getMinutes(), // 分
+            's+': date.getSeconds(), // 秒
+            'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
+            S: date.getMilliseconds() // 毫秒
+        }
+        if (/(y+)/.test(fmt)) {
+            fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
+        }
+        for (var k in o) {
+            if (new RegExp('(' + k + ')').test(fmt)) {
+                fmt = fmt.replace(
+                    RegExp.$1,
+                    RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
+                )
+            }
+        }
+        return fmt
+    }
+}

+ 120 - 0
src.mobile/plugin/axios/constant.js

@@ -0,0 +1,120 @@
+// 服务端地址
+const BASE_URI =
+    process.env.NODE_ENV === 'production' ?
+    process.env.VUE_APP_WEB_API_URL + process.env.VUE_APP_BASE_API :
+    process.env.VUE_APP_BASE_API // 代理模式代理地址
+
+// 系统常量
+const DATE_PATTERN = {
+    DATE_TIME_H: 'yyyy-MM-dd HH:mm:ss',
+    DATE_TIME_h: 'yyyy-MM-dd hh:mm:ss',
+    DATE_TIME_s_h: 'MM-dd hh:mm',
+    DATE: 'yyyy-MM-dd',
+    MONTH: 'yyyy-MM',
+    TIME: 'hh:mm:ss'
+}
+
+const PARAM_TYPE = [{
+        value: 'string',
+        label: 'string'
+    },
+    {
+        value: 'date',
+        label: 'date'
+    },
+    {
+        value: 'time',
+        label: 'time'
+    },
+    {
+        value: 'datetime',
+        label: 'datetime'
+    },
+    {
+        value: 'number',
+        label: 'number'
+    }
+]
+
+const PICKER_OPTION = {
+    shortcuts: [{
+            text: '最近一周',
+            onClick: function (picker) {
+                const end = new Date()
+                const start = new Date()
+                start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
+                picker.$emit('pick', [start, end])
+            }
+        },
+        {
+            text: '最近一个月',
+            onClick: function (picker) {
+                const end = new Date()
+                const start = new Date()
+                start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
+                picker.$emit('pick', [start, end])
+            }
+        },
+        {
+            text: '最近三个月',
+            onClick: function (picker) {
+                const end = new Date()
+                const start = new Date()
+                start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
+                picker.$emit('pick', [start, end])
+            }
+        }
+    ]
+}
+
+const KEY_RESULT = 'result'
+
+const KEY_TOKEN = 'token'
+
+const KEY_USER = 'user_'
+
+const KEY_UID = 'uid_'
+
+const KEY_USER_TYPE = 'userType' // 用户模式 : 平台管理 - 1 , 合作方管理 - 2
+
+const KEY_BIZ = 'biz_'
+
+const KEY_CODE = 'code'
+
+const KEY_MSG = 'msg'
+
+const KEY_MSG_ORDER = 'msgOrder'
+
+const KEY_USER_MENU = 'menu'
+
+// 业务常量
+
+export default {
+    /* 服务端地址*/
+    BASE_URI,
+
+    /* 系统常量*/
+    // 时间模版
+    DATE_PATTERN,
+    // 结果关键字
+    KEY_RESULT,
+    // token关键字
+    KEY_TOKEN,
+    // 缓存用户key
+    KEY_USER,
+    KEY_UID,
+    KEY_BIZ,
+    // 缓存用户类型key
+    KEY_USER_TYPE,
+    KEY_USER_MENU,
+    KEY_CODE,
+    KEY_MSG,
+    KEY_MSG_ORDER,
+    // 系统参数类型
+    PARAM_TYPE,
+    // 时间简易选项
+    PICKER_OPTION
+    // 字典关键字
+
+    /* 业务常量*/
+}

+ 53 - 0
src.mobile/plugin/axios/index.js

@@ -0,0 +1,53 @@
+import axios from 'axios'
+import util from './util'
+import constant from './constant'
+import router from '../../router'
+
+axios.defaults.withCredentials = true
+
+// 创建一个 axios 实例
+const service = axios.create({
+    baseURL: constant.BASE_URI,
+    withCredentials: true,
+    timeout: 20000 // 请求超时时间
+})
+
+// 请求拦截器
+service.interceptors.request.use(
+    config => {
+        // 在请求发送之前做一些处理
+        if (!/^https:\/\/|http:\/\//.test(config.url)) {
+            const token = util.getToken()
+            console.log(token)
+            if (token && token !== 'undefined') {
+                // 让每个请求携带token-- ['Authorization']为自定义key 请根据实际情况自行修改
+                config.headers.Authorization = 'Bearer' + token
+            }
+        }
+        return config
+    },
+    error => {
+        // 发送失败
+        console.log(error)
+        Promise.reject(error)
+    }
+)
+
+// 响应拦截器
+service.interceptors.response.use(
+    response => {
+        const res = response.data
+        if (res.statusCode === -401) {
+            util.removeToken()
+            router.push({
+                name: 'mobileLogin'
+            })
+        }
+        return response
+    },
+    error => {
+        return Promise.reject(error)
+    }
+)
+
+export default service

+ 44 - 0
src.mobile/plugin/axios/util.cookies.js

@@ -0,0 +1,44 @@
+import { merge } from 'lodash'
+import Cookies from 'js-cookie'
+
+const cookies = {}
+
+/**
+ * @description 存储 cookie 值
+ * @param {String} name cookie name
+ * @param {String} value cookie value
+ * @param {Object} setting cookie setting
+ */
+
+cookies.set = function (name = 'default', value = '', cookieSetting = {}) {
+    const currentCookieSetting = {
+        expires: 1
+    }
+    merge(currentCookieSetting, cookieSetting)
+    Cookies.set(`d2admin-${process.env.VUE_APP_VERSION}-${name}`, value, currentCookieSetting)
+}
+
+/**
+ * @description 拿到 cookie 值
+ * @param {String} name cookie name
+ */
+cookies.get = function (name = 'default') {
+    return Cookies.get(`d2admin-${process.env.VUE_APP_VERSION}-${name}`)
+}
+
+/**
+ * @description 拿到 cookie 全部的值
+ */
+cookies.getAll = function () {
+    return Cookies.get()
+}
+
+/**
+ * @description 删除 cookie
+ * @param {String} name cookie name
+ */
+cookies.remove = function (name = 'default') {
+    return Cookies.remove(`d2admin-${process.env.VUE_APP_VERSION}-${name}`)
+}
+
+export default cookies

+ 25 - 0
src.mobile/plugin/axios/util.js

@@ -0,0 +1,25 @@
+import cookies from './util.cookies'
+import Cookies from 'js-cookie'
+
+const util = {
+    cookies,
+    getToken,
+    setToken,
+    removeToken
+}
+
+const TokenKey = 'idea_web_phase_token'
+
+function getToken() {
+    return Cookies.get(TokenKey)
+}
+
+function setToken(token) {
+    return Cookies.set(TokenKey, token)
+}
+
+function removeToken() {
+    return Cookies.remove(TokenKey)
+}
+
+export default util

+ 146 - 0
src.mobile/router/index.js

@@ -0,0 +1,146 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+import util from '@.mobile/plugin/axios/util'
+
+Vue.use(VueRouter)
+
+const routes = [
+    {
+        path: '/',
+        name: 'index',
+        component: () => import('@.mobile/views/mobile-page/mobile-home')
+    },
+    {
+        path: '/login',
+        name: 'login',
+        // component: () => import('@.mobile/views/login.vue')
+        component: () => import('@.mobile/views/mobile-page/mobile-login')
+    },
+    {
+        path: '/logout',
+        name: 'logout',
+        component: () => import('@.mobile/views/logout.vue')
+    },
+    {
+        path: '/report',
+        name: 'report',
+        component: () => import('@.mobile/views/report.vue'),
+        redirect: {
+            name: 'reportMain'
+        },
+        children: [
+            {
+                path: '/reportMain',
+                name: 'reportMain',
+                meta: {
+                    keepAlive: true // 添加这个作为标志符,表明该页面需要保留状态
+                },
+                component: () => import('@.mobile/views/reportMain.vue')
+            },
+            {
+                path: '/reportDetail',
+                name: 'reportDetail',
+                component: () => import('@.mobile/views/reportDetail.vue')
+            }
+        ]
+    },
+    {
+        path: '/mobile-login',
+        name: 'mobileLogin',
+        meta: {
+            title: '登录'
+        },
+        component: () => import('@.mobile/views/mobile-page/mobile-login')
+    },
+    {
+        path: '/mobile-changePsw',
+        name: 'mobilechangePsw',
+        meta: {
+            title: '修改密码'
+        },
+        component: () => import('@.mobile/views/mobile-page/mobile-changePsw')
+    },
+
+    {
+        path: '/mobile-home',
+        name: 'mobileHome',
+        meta: {
+            title: '首页'
+        },
+        component: () => import('@.mobile/views/mobile-page/mobile-home')
+    },
+
+    {
+        path: '/mobile-home',
+        name: 'mobileHome',
+        // meta: {
+        //   title: ''
+        // },
+        component: () => import('@.mobile/views/mobile-page/mobile-home')
+    },
+    {
+        path: '/mobile-myInfor',
+        name: 'mobileMyInfor',
+        meta: {
+            title: '我的信息'
+        },
+        component: () => import('@.mobile/views/mobile-page/mobile-myInfor')
+    },
+    {
+        path: '/mobile-main',
+        name: 'mobileMain',
+        meta: {
+            title: '我的'
+        },
+        component: () => import('@.mobile/views/mobile-page/mobile-main')
+    },
+    {
+        path: '/mobile-estimatedData',
+        name: 'mobileEstimatedData',
+        meta: {
+            title: '月预估数据'
+        },
+        component: () => import('@.mobile/views/mobile-page/mobile-estimatedData')
+    }
+]
+
+const router = new VueRouter({
+    routes
+})
+
+// 免校验token白名单
+const whiteList = ['/mobile-login', '/login']
+
+router.beforeEach(async (to, from, next) => {
+    const token = util.getToken()
+    if (whiteList.indexOf(to.path) === -1) {
+        // 这里暂时将cookie里是否存有token作为验证是否登录的条件
+        // 请根据自身业务需要修改
+        if (token && token !== 'undefined') {
+            next()
+        } else {
+            // 将当前预计打开的页面完整地址临时存储 登录后继续跳转
+            // 这个 cookie(redirect) 会在登录后自动删除
+            util.cookies.set('redirect', to.fullPath)
+            // 没有登录的时候跳转到登录界面
+            next({
+                name: 'mobileLogin'
+            })
+        }
+    } else {
+        if (to.name === 'mobileLogin' || to.name === 'login') {
+            // 如果已经登录,则直接进入系统
+            if (token && token !== undefined) {
+                next(from.path, true)
+            } else {
+                next()
+            }
+        } else {
+            next()
+        }
+    }
+})
+
+router.afterEach(to => {})
+
+export default router

binární
src.mobile/static/img.jpg


+ 10 - 0
src.mobile/store/getters.js

@@ -0,0 +1,10 @@
+const getters = {
+    token: state => state.user.token,
+    avatar: state => state.user.avatar,
+    name: state => state.user.name,
+    roles: state => state.user.roles,
+    perms: state => state.user.perms,
+    title: state => state.layout.title,
+    gValue: state => state.layout.value
+}
+export default getters

+ 25 - 0
src.mobile/store/index.js

@@ -0,0 +1,25 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import getters from './getters'
+
+Vue.use(Vuex)
+
+// https://webpack.js.org/guides/dependency-management/#requirecontext
+const modulesFiles = require.context('./modules', true, /\.js$/)
+
+// you do not need `import app from './modules/app'`
+// it will auto require all vuex module from modules file
+const modules = modulesFiles.keys().reduce((modules, modulePath) => {
+    // set './app.js' => 'app'
+    const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
+    const value = modulesFiles(modulePath)
+    modules[moduleName] = value.default
+    return modules
+}, {})
+
+const store = new Vuex.Store({
+    modules,
+    getters
+})
+
+export default store

+ 57 - 0
src.mobile/store/modules/layout.js

@@ -0,0 +1,57 @@
+const state = {
+    title: '统计报表',
+    level: 0,
+    value: ''
+}
+
+const mutations = {
+    SET_TITLE: (state, title) => {
+        state.title = title
+    },
+    SET_LEVEL: (state, level) => {
+        state.level = level
+    },
+    SET_VALUE: (state, value) => {
+        state.value = value
+    }
+}
+
+const levels = ['统计报表', '数据详情']
+
+const actions = {
+    init({ commit, state }, payload) {
+        return new Promise((resolve, reject) => {
+            commit('SET_TITLE', levels[0])
+            commit('SET_LEVEL', 0)
+        })
+    },
+    next({ commit, state }, payload) {
+        return new Promise((resolve, reject) => {
+            const level = state.level + 1
+            commit('SET_TITLE', levels[level])
+            commit('SET_LEVEL', level)
+            resolve(level)
+        })
+    },
+    pre({ commit, state }, payload) {
+        return new Promise((resolve, reject) => {
+            const level = state.level - 1
+            commit('SET_TITLE', levels[level])
+            commit('SET_LEVEL', level)
+            resolve(level)
+        })
+    },
+    gValue({ commit, state }, payload) {
+        return new Promise((resolve, reject) => {
+            commit('SET_VALUE', payload)
+            resolve()
+        })
+    }
+}
+
+export default {
+    namespaced: true,
+    state,
+    mutations,
+    actions
+}

+ 149 - 0
src.mobile/store/modules/user.js

@@ -0,0 +1,149 @@
+import util from '@.mobile/plugin/axios/util'
+import constant from '@.mobile/plugin/axios/constant'
+import common from '@.mobile/plugin/axios/common'
+import apis from '@.mobile/api/apis'
+
+const state = {
+    token: util.getToken(),
+    name: '',
+    avatar: '',
+    roles: [],
+    perms: []
+}
+
+const mutations = {
+    SET_TOKEN: (state, token) => {
+        state.token = token
+    },
+    SET_NAME: (state, name) => {
+        state.name = name
+    },
+    SET_AVATAR: (state, avatar) => {
+        state.avatar = avatar
+    },
+    SET_ROLES: (state, roles) => {
+        state.roles = roles
+    },
+    SET_PERMS: (state, perms) => {
+        state.perms = perms
+    }
+}
+
+const actions = {
+    // user login
+    login({ commit }, userInfo) {
+        const { username, password, validateCode, captchaId, url, checked } = userInfo
+        return new Promise((resolve, reject) => {
+            const postData = {
+                username: username,
+                password: password,
+                code: validateCode,
+                rememberMe: checked
+            }
+            if (captchaId) {
+                postData.captchaId = captchaId
+            }
+            apis.login('api/ops', url, postData)
+                .then(res => {
+                    if (res.data[constant.KEY_RESULT]) {
+                        const { data } = res
+                        const user = data[constant.KEY_USER]
+                        common.setUser(user)
+                        if (user && user.isInit === '1') {
+                            commit('SET_TOKEN', data[constant.KEY_TOKEN])
+                            util.setToken(data[constant.KEY_TOKEN])
+                        } else {
+                            reject({ isInit: '0' })
+                        }
+                    }
+                    resolve(res.data)
+                })
+                .catch((err, x) => {
+                    reject(err)
+                })
+        })
+    },
+    // user login
+    login1({ commit }, userInfo) {
+        const { username, password, validateCode, captchaId, url, checked } = userInfo
+        return new Promise((resolve, reject) => {
+            const postData = {
+                userName: username,
+                password: password,
+                code: validateCode,
+                rememberMe: checked
+            }
+            if (captchaId) {
+                postData.captchaId = captchaId
+            }
+            apis.login('wx/auth', url, postData)
+                .then(res => {
+                    console.log(res)
+                    if (res.data[constant.KEY_RESULT]) {
+                        console.log(res.data)
+                        const { data } = res
+                        const user = data[constant.KEY_USER]
+                        common.setUser(user)
+                        if (user) {
+                            commit('SET_TOKEN', data[constant.KEY_TOKEN])
+                            util.setToken(data[constant.KEY_TOKEN])
+                        } else {
+                            reject('登录失败,未获取到用户信息')
+                        }
+                    }
+                    resolve(res.data)
+                })
+                .catch((err, x) => {
+                    reject(err)
+                })
+        })
+    },
+
+    // user logout
+    logout({ commit, state }) {
+        return new Promise((resolve, reject) => {
+            apis.logout('web', 'logout', '')
+                .then(logout => {
+                    commit('SET_TOKEN', '')
+                    commit('SET_NAME', '')
+                    commit('SET_AVATAR', '')
+                    common.removeStorage(common.MenuKey())
+                    common.removeStorage(common.UserKey())
+                    common.removeStorage(common.BizKey())
+                    util.removeToken()
+                    resolve()
+                })
+                .catch(error => {
+                    reject(error)
+                })
+        })
+    },
+    cleanCache({ commit, state }) {
+        return new Promise((resolve, reject) => {
+            commit('SET_TOKEN', '')
+            commit('SET_NAME', '')
+            commit('SET_AVATAR', '')
+            common.removeStorage(common.MenuKey())
+            common.removeStorage(common.UserKey())
+            common.removeStorage(common.BizKey())
+            util.removeToken()
+            resolve()
+        })
+    },
+    // remove token
+    resetToken({ commit }) {
+        return new Promise(resolve => {
+            commit('SET_TOKEN', '')
+            localStorage.removeItem(constant.KEY_USER + common.currUserType())
+            util.removeToken()
+            resolve()
+        })
+    }
+}
+
+export default {
+    namespaced: true,
+    state,
+    mutations,
+    actions
+}

+ 62 - 0
src.mobile/vant.js

@@ -0,0 +1,62 @@
+import Vue from 'vue'
+
+import {
+    Col,
+    Row,
+    Button,
+    Image as VanImage,
+    Lazyload,
+    Field,
+    CellGroup,
+    Cell,
+    Checkbox
+} from 'vant'
+Vue.use(Col)
+Vue.use(Row)
+Vue.use(Button)
+Vue.use(VanImage)
+Vue.use(Lazyload)
+Vue.use(Field)
+Vue.use(CellGroup)
+Vue.use(Cell)
+Vue.use(Checkbox)
+
+import { Swipe, SwipeItem } from 'vant'
+
+Vue.use(Swipe)
+Vue.use(SwipeItem)
+
+import { NavBar } from 'vant'
+
+Vue.use(NavBar)
+
+import { Grid, GridItem, Picker, Popup, Search } from 'vant'
+
+Vue.use(Grid)
+Vue.use(GridItem)
+Vue.use(Picker)
+Vue.use(Popup)
+Vue.use(Search)
+
+import { Tab, Tabs } from 'vant'
+
+Vue.use(Tab)
+Vue.use(Tabs)
+
+import { PullRefresh } from 'vant'
+
+Vue.use(PullRefresh)
+
+import { List } from 'vant'
+
+Vue.use(List)
+
+import { Tabbar, TabbarItem, Icon } from 'vant'
+
+Vue.use(Tabbar)
+Vue.use(TabbarItem)
+Vue.use(Icon)
+
+import { DatetimePicker } from 'vant'
+
+Vue.use(DatetimePicker)

+ 229 - 0
src.mobile/views/FireTable.vue

@@ -0,0 +1,229 @@
+<template>
+    <div>
+        <van-search
+            v-model="value"
+            placeholder="姓名、部门、类型模糊查询"
+            input-align="center"
+            left-icon=""
+            show-action
+            shape="round"
+            @search="onSearch"
+        >
+            <template #action>
+                <div @click="onSearch"><van-icon name="search" />搜索</div>
+            </template>
+        </van-search>
+        <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
+            <van-list
+                v-model="loading"
+                :finished="finished"
+                finished-text="没有更多了"
+                @load="onLoad"
+            >
+                <van-cell style="background-color: #ca8caf">
+                    <template #title>
+                        <van-row>
+                            <van-col
+                                v-for="(column, idx) in columns"
+                                :key="column.name + idx"
+                                :span="24 / columns.length"
+                            >
+                                {{ column.name }}
+                                <van-icon :name="column.icon" @click="onSort(column.key)" />
+                            </van-col>
+                        </van-row>
+                    </template>
+                </van-cell>
+
+                <van-cell v-for="item in list" :key="item">
+                    <template #title>
+                        <van-row>
+                            <van-col
+                                v-for="(column, idx) in columns"
+                                :key="column.key + idx"
+                                :span="24 / columns.length"
+                            >
+                                {{ item[column.key] }}
+                            </van-col>
+                        </van-row>
+                    </template>
+                </van-cell>
+            </van-list>
+        </van-pull-refresh>
+    </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+
+export default {
+    name: 'FireTable',
+    props: {
+        queryType: {
+            type: String,
+            default: '1'
+        },
+        allColumns: {
+            type: Boolean,
+            default: true
+        }
+    },
+    data() {
+        return {
+            value: '',
+            list: [],
+            loading: false,
+            finished: false,
+            refreshing: false,
+            page: 1,
+            sorts: ['', 'desc', 'asc'],
+            icons: ['sort', 'descending', 'ascending'],
+            columns: [
+                {
+                    name: '部门',
+                    key: 'bm',
+                    sort: '',
+                    icon: 'sort'
+                },
+                {
+                    name: '姓名',
+                    key: 'xm',
+                    sort: '',
+                    icon: 'sort'
+                },
+                {
+                    name: '类型',
+                    key: 'lx',
+                    sort: '',
+                    icon: 'sort'
+                },
+                {
+                    name: '签到时间',
+                    key: 'qdrq',
+                    sort: '',
+                    icon: 'sort'
+                }
+            ]
+        }
+    },
+    computed: {
+        ...mapGetters(['gValue'])
+    },
+    watch: {
+        gValue: function (n, o) {
+            if (n) {
+                this.finished = false
+                this.loading = true
+                this.onLoad(1)
+            }
+        }
+    },
+    mounted() {
+        if (!this.allColumns) {
+            this.columns = [
+                {
+                    name: '部门',
+                    key: 'bm',
+                    sort: '',
+                    icon: 'sort'
+                },
+                {
+                    name: '姓名',
+                    key: 'xm',
+                    sort: '',
+                    icon: 'sort'
+                },
+                {
+                    name: '类型',
+                    key: 'lx',
+                    sort: '',
+                    icon: 'sort'
+                }
+            ]
+        }
+    },
+    methods: {
+        onLoad(page) {
+            if (!this.gValue) {
+                this.finished = true
+                this.loading = false
+                return
+            }
+            if (page) {
+                this.page = page
+            }
+            page = this.page
+            this.page += 1
+
+            let sort = ''
+            this.columns.forEach(function (v) {
+                if (v.sort) {
+                    sort += v.key + ' ' + v.sort
+                }
+            })
+
+            // 查询类型 queryType
+            // 演习id=gValue
+            // 排序情况= columns . sort
+            // 搜索情况 this.value
+            const postData = {
+                yxid: this.gValue,
+                cxlx: this.queryType,
+                sort: sort,
+                gjz: this.value,
+                page: page
+            }
+
+            this.$apis.requestBase('list', postData).then(res => {
+                if (res.data && res.data.length > 0) {
+                    if (page === 1) {
+                        this.list = res.data
+                    } else {
+                        this.list.push(res.data)
+                    }
+                } else {
+                    if (page === 1) {
+                        this.list = []
+                    }
+                    this.finished = true
+                }
+                this.loading = false
+                if (this.refreshing) {
+                    this.refreshing = false
+                }
+            })
+        },
+        onRefresh() {
+            this.finished = false
+            this.loading = true
+            this.onLoad(1)
+        },
+        onSearch() {
+            this.finished = false
+            this.loading = true
+            this.onLoad(1)
+        },
+        onSort(key) {
+            const _this = this
+            this.columns.forEach(function (v) {
+                if (v.key === key) {
+                    let idx = _this.sorts.indexOf(v.sort) + 1
+                    if (idx > 2) {
+                        idx = 0
+                    }
+                    v.sort = _this.sorts[idx]
+                    v.icon = _this.icons[idx]
+                } else {
+                    v.sort = _this.sorts[0]
+                    v.icon = _this.icons[0]
+                }
+            })
+            this.finished = false
+            this.loading = true
+            this.onLoad(1)
+        }
+    }
+}
+</script>
+
+<style scoped></style>

+ 66 - 0
src.mobile/views/index.vue

@@ -0,0 +1,66 @@
+<template>
+    <div>
+        <van-nav-bar title="首页" />
+        <van-row>
+            <van-col span="24">
+                <van-swipe :autoplay="3000" :width="'100%'">
+                    <van-swipe-item v-for="(image, index) in images" :key="index">
+                        <img width="100%" height="100%" :src="image" />
+                    </van-swipe-item>
+                </van-swipe>
+            </van-col>
+        </van-row>
+        <div style="height: 500px">
+            <van-grid :column-num="3" clickable :gutter="10" style="margin-top: 20px">
+                <van-grid-item
+                    style="color: #b2568c"
+                    v-for="value in items"
+                    :key="value.name"
+                    :text="value.name"
+                    :to="value.to"
+                    :icon="value.icon"
+                >
+                </van-grid-item>
+            </van-grid>
+        </div>
+        <van-tabbar route active-color="#B2568C" inactive-color="#999">
+            <van-tabbar-item replace to="/" icon="wap-home"> 首页 </van-tabbar-item>
+            <van-tabbar-item replace to="/sign" icon="user-o"> 签到 </van-tabbar-item>
+        </van-tabbar>
+    </div>
+</template>
+
+<script>
+import img from '../static/img.jpg'
+export default {
+    name: 'Main',
+    data() {
+        return {
+            images: [img],
+            items: [
+                {
+                    name: '统计报表',
+                    img: 'https://cdn.jsdelivr.net/npm/@vant/assets/cat.jpeg',
+                    to: '/report',
+                    icon: 'chart-trending-o'
+                },
+                {
+                    name: '签到打卡',
+                    img: 'https://cdn.jsdelivr.net/npm/@vant/assets/cat.jpeg',
+                    to: '/sign',
+                    icon: 'todo-list'
+                },
+                {
+                    name: '退出登录',
+                    img: 'https://cdn.jsdelivr.net/npm/@vant/assets/cat.jpeg',
+                    to: '/logout',
+                    icon: 'replay'
+                }
+            ]
+        }
+    },
+    watch: {},
+    mounted() {},
+    methods: {}
+}
+</script>

+ 150 - 0
src.mobile/views/login.vue

@@ -0,0 +1,150 @@
+<template>
+    <div style="background-color: white">
+        <van-row>
+            <van-col span="24">
+                <van-swipe :autoplay="3000" :width="'100%'">
+                    <van-swipe-item v-for="(image, index) in images" :key="index">
+                        <img width="100%" height="100%" :src="image" />
+                    </van-swipe-item>
+                </van-swipe>
+            </van-col>
+        </van-row>
+        <van-row>
+            <van-col span="18" offset="3">
+                <van-cell-group>
+                    <br />
+                    <van-field
+                        v-model="loginForm.username"
+                        input-align="center"
+                        center
+                        placeholder="请输入用户名"
+                        clearable
+                        style="border-bottom: 1px solid #b2568c"
+                    >
+                        <template slot="left-icon">
+                            <van-icon name="manager" color="#B2568C" size="20" />
+                        </template>
+                    </van-field>
+                    <br />
+                    <van-field
+                        v-model="loginForm.password"
+                        input-align="center"
+                        center
+                        type="password"
+                        placeholder="请输入密码"
+                        clearable
+                        style="border-bottom: 1px solid #b2568c"
+                    >
+                        <template slot="left-icon">
+                            <van-icon name="eye" color="#B2568C" size="20" />
+                        </template>
+                    </van-field>
+                    <!--                    <van-field v-model="loginForm.validateCode" clearable label="验证码" placeholder="请输入验证码">-->
+                    <!--                        <template #button>-->
+                    <img
+                        id="codeImg"
+                        style="height: 0px; position: absolute"
+                        alt="点击更换"
+                        title="点击更换"
+                        :src="loginForm.captchaImage"
+                        @click="captchaImageRefresh(loginForm)"
+                    />
+                    <!--                        </template>-->
+                    <!--                    </van-field>-->
+                    <br />
+                    <van-cell>
+                        <template #right-icon>
+                            <van-checkbox
+                                checked-color="#B2568C"
+                                v-model="loginForm.checked"
+                                icon-size="16px"
+                            >
+                                记住密码
+                            </van-checkbox>
+                        </template>
+                    </van-cell>
+                    <br />
+                    <van-button
+                        color="#B2568C"
+                        :loading="loading"
+                        round
+                        type="info"
+                        block
+                        @click="loginClick"
+                    >
+                        登录
+                    </van-button>
+                </van-cell-group>
+            </van-col>
+        </van-row>
+    </div>
+</template>
+
+<script>
+import { Toast } from 'vant'
+import img from '../static/img.jpg'
+
+export default {
+    name: 'Login',
+    data() {
+        return {
+            loginForm: {
+                username: 'admin',
+                password: '',
+                validateCode: '',
+                captchaId: '',
+                captchaImage: '',
+                url: 'login',
+                checked: true
+            },
+            loading: false,
+            images: [img]
+        }
+    },
+    watch: {},
+    created() {
+        this.loginForm.checked = localStorage.getItem('remember') === '1'
+        if (this.loginForm.checked) {
+            this.loginForm.username = localStorage.getItem('username')
+            this.loginForm.password = localStorage.getItem('password')
+        }
+    },
+    mounted() {
+        this.$store.dispatch('user/cleanCache')
+        this.captchaImageRefresh(this.loginForm)
+    },
+    methods: {
+        loginClick: function () {
+            this.loading = true
+            localStorage.setItem('remember', this.loginForm.checked ? '1' : '0')
+            localStorage.setItem('username', this.loginForm.username)
+            localStorage.setItem('password', this.loginForm.password)
+            this.$store
+                .dispatch('user/login', this.loginForm)
+                .then(res => {
+                    if (res.result) {
+                        this.$router.push('/')
+                        this.loading = false
+                        Toast('登录成功')
+                    } else {
+                        Toast(res.msg)
+                        this.loading = false
+                    }
+                })
+                .catch(err => {
+                    Toast(err)
+                    this.loading = false
+                })
+        },
+        captchaImageRefresh: function (_form) {
+            _form.captchaId = this.$common.uuid(8)
+            _form.captchaImage =
+                this.$constant.BASE_URI +
+                '/captcha/captchaImage?type=math&captchaId=' +
+                _form.captchaId +
+                '&s=' +
+                Math.random()
+        }
+    }
+}
+</script>

+ 22 - 0
src.mobile/views/logout.vue

@@ -0,0 +1,22 @@
+<template>
+    <div></div>
+</template>
+
+<script>
+import { Toast } from 'vant'
+
+export default {
+    name: 'Logout',
+    data() {
+        return {}
+    },
+    watch: {},
+    mounted() {
+        this.$store.dispatch('user/cleanCache').then(res => {
+            Toast('退出成功')
+            this.$router.push('/login')
+        })
+    },
+    methods: {}
+}
+</script>

+ 234 - 0
src.mobile/views/mobile-page/components/data-filling.vue

@@ -0,0 +1,234 @@
+<template>
+    <div>
+        <van-row style="margin-top: 10px">
+            <van-col :span="22" :offset="1" style="background-color: white">
+                <div class="inforBox">
+                    <p>归属年月:</p>
+                    <div>
+                        <!-- <van-datetime-picker-->
+                        <!--     v-model="form.yf"-->
+                        <!--     type="year-month"-->
+                        <!--     title="选择年月"-->
+                        <!--     :min-date="minDate"-->
+                        <!--     :max-date="maxDate"-->
+                        <!-- />-->
+
+                        <van-field
+                            right-icon="arrow-down"
+                            v-model="form.yf"
+                            placeholder="根据任务自动带出年、月"
+                        />
+                    </div>
+                </div>
+                <div class="inforBox" v-if="!form.isCf">
+                    <p class="dataName">
+                        <span>*</span>
+                        本月营业收入
+                        <span class="Company">(万元)</span>
+                    </p>
+                    <input v-model="form.yysr" type="number" class="input" placeholder="请填写" />
+                </div>
+                <div class="inforBox" v-if="form.isCf">
+                    <p class="dataName">
+                        <span>*</span>
+                        本月餐费收入
+                        <span class="Company">(万元)</span>
+                    </p>
+                    <input v-model="form.cfsr" type="number" class="input" placeholder="请填写" />
+                </div>
+                <div class="inforBox">
+                    <p class="dataName">
+                        <span>*</span>
+                        本月总薪资
+                        <span class="Company">(万元)</span>
+                    </p>
+                    <input v-model="form.xzze" type="number" class="input" placeholder="请填写" />
+                </div>
+                <div class="inforBox" v-if="!form.isCf">
+                    <p class="dataName">
+                        <span>*</span>
+                        上年本月营业收入
+                        <span class="Company">(万元)</span>
+                    </p>
+                    <input
+                        v-model="form.qntqyysr"
+                        type="number"
+                        class="input"
+                        placeholder="请填写/或自动带出"
+                    />
+                </div>
+                <div class="inforBox" v-if="form.isCf">
+                    <p class="dataName">
+                        <span>*</span>
+                        上年本月餐费收入
+                        <span class="Company">(万元)</span>
+                    </p>
+                    <input
+                        v-model="form.qntqyysr"
+                        type="number"
+                        class="input"
+                        placeholder="请填写/或自动带出"
+                    />
+                </div>
+                <div class="inforBox">
+                    <div style="margin: 20px 0; text-align: center">
+                        <van-button round type="primary" color="rgb(227 239 254)" @click="toHome">
+                            <p class="off">返回</p>
+                        </van-button>
+                        <van-button round type="primary" color="rgb(26 76 218)" @click="save">
+                            <p class="submit">提交</p>
+                        </van-button>
+                    </div>
+                </div>
+            </van-col>
+        </van-row>
+    </div>
+</template>
+<script>
+import { Toast } from 'vant'
+import apis from '@.mobile/api/apis'
+import common from '@.mobile/plugin/axios/common'
+export default {
+    data() {
+        return {
+            minDate: new Date(2000, 0, 1),
+            maxDate: new Date(),
+            form: {
+                rowguid: '',
+                qymc: '',
+                shxydm: '',
+                tblx: '预估填报',
+                yf: this.$common.formatDate('yyyyMM', new Date()),
+                tbsj: '',
+                yysr: '',
+                qntqyysr: '',
+                xzze: '',
+                cfsr: '',
+                dxcb: '未发送',
+                tbrid: '--',
+                tbr: '--',
+                ejgm: '--',
+                yjgm: '--',
+                lxdh: '--',
+                qylxr: '--',
+                qyzt: '--',
+                ssbk: '--',
+                isCf: false
+            }
+        }
+    },
+    mounted() {
+        const user = common.currUser()
+        this.form.qymc = user.qymc
+        this.form.shxydm = user.shxydm
+        this.form.ejgm = user.ejgm
+        this.form.yjgm = user.yjgm
+        this.form.lxdh = user.lxdh
+        this.form.qylxr = user.qylxr
+        this.form.qyzt = user.qyzt
+        this.form.ssbk = user.ssbk
+        this.form.qntqyysr = user.qntqyysr
+        console.log(user.hymc)
+        this.form.isCf = user.hymc === '住宿餐饮'
+    },
+    methods: {
+        toHome() {
+            this.$router.push('/mobile-home')
+        },
+        save() {
+            if (!this.form.shxydm) {
+                Toast('请重新登录后重试')
+                this.$router.push('/mobile-login')
+                return
+            }
+            if (this.form.isCf) {
+                if (!this.form.cfsr) {
+                    Toast('请填写餐费收入')
+                    return
+                }
+            } else {
+                if (!this.form.yysr) {
+                    Toast('请填写营业收入')
+                    return
+                }
+            }
+
+            if (!this.form.xzze) {
+                Toast('请填写薪资总额')
+                return
+            }
+            apis.requestData('EcCompanyPredictController', 'save', this.form).then(res => {
+                const data = res.data
+                if (data.code == 200) {
+                    Toast('预估数据填报成功')
+                } else {
+                    Toast(data.msg)
+                }
+            })
+        }
+    }
+}
+</script>
+<style scoped>
+.inforBody {
+    background: white;
+    margin: 10vw 5vw;
+    padding: 0 0 5vw 0;
+}
+.inforBox {
+    padding: 1vw 5vw;
+    position: relative;
+}
+.input {
+    width: 100%;
+    padding: 1vw;
+    border: 1px solid #e6e6e6;
+}
+.van-field {
+    width: 100%;
+    padding: 1vw;
+    border: 1px solid #e6e6e6;
+}
+.down {
+}
+.dataName::first-letter {
+    color: #ff4343;
+}
+.Company {
+    color: #999999;
+}
+
+.buttonBox {
+    display: flex;
+    justify-content: space-evenly;
+}
+
+.van-button {
+    width: 35vw;
+    cursor: pointer;
+    border: 1px solid rgb(209 220 250);
+}
+input::-webkit-input-placeholder {
+    color: #cccccc;
+    font-size: 2.5vw;
+}
+::v-deep .van-field__control::-webkit-input-placeholder {
+    color: #cccccc;
+    font-size: 2.5vw;
+}
+.off {
+    display: flex;
+    justify-content: center;
+    color: rgb(66 107 224);
+    width: 30vw;
+    letter-spacing: 2vw;
+    margin-left: 1vw;
+}
+.submit {
+    display: flex;
+    justify-content: center;
+    width: 30vw;
+    letter-spacing: 2vw;
+    margin-left: 1vw;
+}
+</style>

+ 153 - 0
src.mobile/views/mobile-page/components/historical-filling.vue

@@ -0,0 +1,153 @@
+<template>
+    <div class="page-body">
+        <van-row style="margin-top: 10px">
+            <van-col :span="22" :offset="1" style="background-color: white">
+                <van-cell
+                    :title="listQuery.yf"
+                    @click="setYearBoxStatus(true)"
+                    is-link
+                    arrow-direction="down"
+                ></van-cell>
+            </van-col>
+        </van-row>
+        <van-row style="margin-top: 10px">
+            <van-col :span="22" :offset="1" style="background-color: white">
+                <div class="itemBox" v-for="(item, index) in dataList" :key="index">
+                    <div class="timeBox">
+                        <div class="time">{{ item.yf }}</div>
+                        <div class="subTime">{{ item.tbsj }}</div>
+                    </div>
+                    <div v-if="!isCf">
+                        <div>本月营业收入<span class="Company">(万元)</span>:</div>
+                        <div class="value">{{ item.yysr }}</div>
+                    </div>
+                    <div v-if="isCf">
+                        <div>本月餐费收入<span class="Company">(万元)</span>:</div>
+                        <div class="value">{{ item.cfsr }}</div>
+                    </div>
+                    <div>
+                        <div>本月总薪资<span class="Company">(万元)</span>:</div>
+                        <div class="value">{{ item.xzze }}</div>
+                    </div>
+                    <!-- <div>
+                        <div>本月餐费收入<span class="Company">(万元)</span>:</div>
+                        <div class="value">{{ item.cfsr }}</div>
+                    </div> -->
+                </div>
+            </van-col>
+        </van-row>
+        <!-- 弹框 -->
+        <van-popup v-model="show" position="bottom">
+            <van-picker
+                show-toolbar
+                :columns="yearColumns"
+                :default-index="yearColumns.length"
+                @confirm="getYear"
+                @cancel="cancel"
+            />
+        </van-popup>
+    </div>
+</template>
+<script>
+import common from '@.mobile/plugin/axios/common'
+import apis from '@.mobile/api/apis'
+import { Toast } from 'vant'
+
+export default {
+    data() {
+        return {
+            currentDate: '',
+            show: false,
+            yearColumns: [],
+            dataList: [],
+            isCf: false,
+            listQuery: {
+                yf: '',
+                tblx: '预估填报',
+                shxydm: '',
+                qymc: '',
+                pageNum: 1,
+                pageSize: 20,
+                controller: 'EcCompanyPredictController'
+            }
+        }
+    },
+    mounted() {
+        const user = common.currUser()
+        this.listQuery.shxydm = user.shxydm
+        this.listQuery.qymc = user.qymc
+        this.yearData()
+        this.list()
+
+        this.isCf = user.hymc === '住宿餐饮'
+    },
+    methods: {
+        list() {
+            apis.requestData('EcCompanyPredictController', 'list', this.listQuery).then(res => {
+                this.dataList = res.data.rows
+            })
+        },
+        setYearBoxStatus(e) {
+            this.show = e
+        },
+        yearData() {
+            const year = new Date().getFullYear()
+            const yearColumns = []
+            for (let i = 2000; i <= year; i++) {
+                yearColumns.push(i)
+            }
+            this.yearColumns = yearColumns
+            this.listQuery.yf = this.yearColumns.find(e => e === Number(year))
+        },
+        getYear(e) {
+            this.listQuery.yf = e
+            this.list()
+            this.setYearBoxStatus(false)
+        },
+        cancel() {
+            this.setYearBoxStatus(false)
+        }
+    }
+}
+</script>
+<style scoped>
+.itemBox {
+    background: white;
+    border-radius: 1vw;
+    display: flex;
+    flex-direction: column;
+    padding: 1vw 4vw;
+}
+
+.itemBox > div {
+    margin: 2vw 0;
+    display: flex;
+    font-size: 3vw;
+}
+
+.timeBox {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+}
+
+.time {
+    font-size: 4vw;
+    font-weight: 600;
+    color: #333333;
+}
+
+.subTime {
+    color: #cccccc;
+    font-size: 2.5vw;
+}
+
+.Company {
+    color: #999999;
+}
+
+.value {
+    color: #1a4cda;
+    font-weight: 600;
+}
+</style>

+ 107 - 0
src.mobile/views/mobile-page/mobile-changePsw.vue

@@ -0,0 +1,107 @@
+<template>
+    <div style="height: 100vh; width: 100vw">
+        <van-nav-bar title="密码修改" @click-left="toMain">
+            <template #left>
+                <van-icon name="arrow-left" />
+            </template>
+        </van-nav-bar>
+        <van-row style="margin-top: 10%; z-index: 99">
+            <van-col :span="22" :offset="1" style="border-radius: 10px; overflow: hidden">
+                <van-cell-group class="bx">
+                    <br />
+                    <van-field
+                        v-model="password.wxmm"
+                        label="当前密码:"
+                        placeholder="请输入当前密码"
+                        type="password"
+                    />
+                    <van-field
+                        v-model="password.newWxmm"
+                        label="修改密码:"
+                        placeholder="密码不少于6位,需要字母与数字构成"
+                        type="password"
+                    />
+                    <van-field
+                        v-model="password.repeatPsw"
+                        label="确认修改密码:"
+                        placeholder="密码不少于6位,需要字母与数字构成"
+                        type="password"
+                    />
+                    <br />
+                    <van-button
+                        round
+                        block
+                        @click="save"
+                        type="info"
+                        style="width: 50%; margin: 0 auto"
+                    >
+                        修改
+                    </van-button>
+                    <br />
+                </van-cell-group>
+            </van-col>
+        </van-row>
+    </div>
+</template>
+<script>
+import { Toast } from 'vant'
+import apis from '@.mobile/api/apis'
+import common from '@.mobile/plugin/axios/common'
+
+export default {
+    data() {
+        return {
+            password: {
+                rowguid: '',
+                wxmm: '',
+                newWxmm: '',
+                repeatPsw: ''
+            }
+        }
+    },
+    mounted() {
+        const user = common.currUser()
+        if (user && user.rowguid) {
+            this.password.rowguid = user.rowguid
+        } else {
+            this.$router.push('/mobile-login')
+        }
+    },
+    methods: {
+        toMain() {
+            this.$router.push('/mobile-main')
+        },
+        save() {
+            if (this.password.newWxmm !== this.password.repeatPsw) {
+                Toast('两次输入的密码不一致')
+                return
+            }
+
+            apis.requestData('EcCompanyInfoController', 'editPwd', this.password).then(res => {
+                if (res.data.code === 200) {
+                    Toast('密码修改成功,请重新登录')
+                    this.$store.dispatch('user/cleanCache').then(res => {
+                        this.$router.push('/mobile-login')
+                    })
+                } else {
+                    Toast(res.data.msg)
+                }
+            })
+        }
+    }
+}
+</script>
+<style scoped>
+::v-deep .van-cell {
+    height: 60px;
+    line-height: 60px;
+}
+::v-deep .van-cell__title {
+    height: 60px;
+    line-height: 60px;
+}
+::v-deep .van-cell__right-icon {
+    height: 60px;
+    line-height: 60px;
+}
+</style>

+ 40 - 0
src.mobile/views/mobile-page/mobile-estimatedData.vue

@@ -0,0 +1,40 @@
+<template>
+    <div style="height: 100vh; width: 100vw">
+        <van-nav-bar title="月预估数据" @click-left="toHome">
+            <template #left>
+                <van-icon name="arrow-left" />
+            </template>
+        </van-nav-bar>
+        <van-row>
+            <van-col span="24">
+                <van-tabs v-model="select">
+                    <van-tab name="a" title="数据填报">
+                        <dataFilling></dataFilling>
+                    </van-tab>
+                    <van-tab name="b" title="历史填报">
+                        <historicalFilling></historicalFilling>
+                    </van-tab>
+                </van-tabs>
+            </van-col>
+        </van-row>
+    </div>
+</template>
+<script>
+import dataFilling from './components/data-filling.vue'
+import historicalFilling from './components/historical-filling.vue'
+export default {
+    components: {
+        dataFilling,
+        historicalFilling
+    },
+    data() {
+        return { select: 'a' }
+    },
+    methods: {
+        toHome() {
+            this.$router.push('/mobile-home')
+        }
+    }
+}
+</script>
+<style scoped></style>

+ 119 - 0
src.mobile/views/mobile-page/mobile-home.vue

@@ -0,0 +1,119 @@
+<template>
+    <div style="height: 100vh; width: 100vw">
+        <van-nav-bar title="首页" />
+        <van-row>
+            <van-col span="24">
+                <div class="bgImg"></div>
+            </van-col>
+        </van-row>
+        <van-row>
+            <van-grid :column-num="3" clickable :gutter="10" style="margin-top: 20px">
+                <van-grid-item to="/mobile-estimatedData">
+                    <div style="position: relative; height: 120px; width: 100%">
+                        <img
+                            src="../../assets/mobile-Page-Image/home_slices/z_459.png"
+                            alt=""
+                            style="width: 100%; height: 100%; position: absolute"
+                        />
+                        <span
+                            style="
+                                position: absolute;
+                                bottom: 10px;
+                                width: 100%;
+                                text-align: center;
+                                color: #999;
+                            "
+                        >
+                            月预估数据
+                        </span>
+                    </div>
+                </van-grid-item>
+                <van-grid-item @click="callService">
+                    <div style="position: relative; height: 120px; width: 100%">
+                        <img
+                            src="../../assets/mobile-Page-Image/home_slices/z_460.png"
+                            alt=""
+                            style="width: 100%; height: 100%; position: absolute"
+                        />
+                        <span
+                            style="
+                                position: absolute;
+                                bottom: 10px;
+                                width: 100%;
+                                text-align: center;
+                                color: #999;
+                            "
+                        >
+                            联系服务专员
+                        </span>
+                    </div>
+                </van-grid-item>
+            </van-grid>
+        </van-row>
+        <van-tabbar route active-color="#B2568C" inactive-color="#999">
+            <van-tabbar-item replace to="/mobile-home" icon="wap-home">
+                <span>首页</span>
+                <template #icon="props">
+                    <img src="../../assets/mobile-Page-Image/home_slices/z_127.png" />
+                </template>
+            </van-tabbar-item>
+            <van-tabbar-item replace to="/mobile-main" icon="user-o">
+                <span>我的</span>
+                <template #icon="props">
+                    <img src="../../assets/mobile-Page-Image/home_slices/z_134.png" />
+                </template>
+            </van-tabbar-item>
+        </van-tabbar>
+    </div>
+</template>
+<script>
+import { Dialog } from 'vant'
+import { Toast } from 'vant'
+import apis from '@.mobile/api/apis'
+import common from '@.mobile/plugin/axios/common'
+
+export default {
+    data() {
+        return {
+            phoneNum: ''
+        }
+    },
+    mounted() {
+        const user = common.currUser()
+        if (user && user.qyfwzyid) {
+            console.log(user.qyfwzyid)
+            this.phoneNum = user.qyfwzyid
+        }
+    },
+    methods: {
+        callService() {
+            let content = '您确认拨打' + this.phoneNum + '吗?'
+            Dialog.confirm({
+                title: '拨打电话',
+                message: content
+            }).then(action => {
+                window.location.href = 'tel://' + this.phoneNum
+            })
+        },
+        toMain() {
+            this.$router.push('/mobile-main')
+        },
+        toEstimated() {
+            this.$router.push('/mobile-estimatedData')
+        }
+    }
+}
+</script>
+<style scoped>
+.bgImg {
+    background: url(../../assets/mobile-Page-Image/home_slices/mbz2.png) no-repeat;
+    width: 100vw;
+    height: 40vw;
+    background-size: 100%;
+}
+
+::v-deep .van-grid-item__content {
+    padding: 0;
+    border-radius: 10px;
+}
+</style>

+ 178 - 0
src.mobile/views/mobile-page/mobile-login.vue

@@ -0,0 +1,178 @@
+<template>
+    <div class="page-body">
+        <van-row>
+            <van-col span="20" offset="2">
+                <div style="height: 25vh; font-size: 2rem; font-weight: 600">
+                    <div style="line-height: 15vh">旺庄统计平台</div>
+                    <div class="client">(企业客户端)</div>
+                </div>
+            </van-col>
+        </van-row>
+        <van-row>
+            <van-col span="22" offset="1">
+                <van-cell-group style="padding: 10px 20px; border-radius: 10px">
+                    <br />
+                    <van-field
+                        v-model="loginForm.username"
+                        input-align="left"
+                        center
+                        label="用户名:"
+                        placeholder="输入用户名"
+                        clearable
+                    >
+                    </van-field>
+                    <van-field
+                        v-model="loginForm.password"
+                        input-align="left"
+                        center
+                        label="  密  码  :"
+                        type="password"
+                        placeholder="输入密码"
+                        clearable
+                    >
+                    </van-field>
+                    <van-field
+                        v-model="loginForm.validateCode"
+                        input-align="left"
+                        clearable
+                        placeholder="输入验证码"
+                    >
+                        <template #label>
+                            <span style="line-height: 32px">验证码:</span>
+                        </template>
+                        <template #button>
+                            <img
+                                id="codeImg"
+                                alt="点击更换"
+                                title="点击更换"
+                                :src="loginForm.captchaImage"
+                                @click="captchaImageRefresh(loginForm)"
+                            />
+                        </template>
+                    </van-field>
+                    <van-cell class="mmm">
+                        <template #right-icon>
+                            <van-checkbox
+                                checked-color="#1989fa"
+                                v-model="loginForm.checked"
+                                icon-size="16px"
+                            >
+                                记住密码
+                            </van-checkbox>
+                        </template>
+                    </van-cell>
+                    <br />
+                    <br />
+                    <van-button
+                        :loading="loading"
+                        style="width: 40%; margin: 0 auto"
+                        round
+                        type="info"
+                        block
+                        @click="loginClick"
+                    >
+                        登录
+                    </van-button>
+                    <br />
+                    <br />
+                </van-cell-group>
+            </van-col>
+        </van-row>
+        <div class="tips">2022@copyrigth 旺庄街道统计管理平台 tech. supported by idea-sf.com</div>
+    </div>
+</template>
+
+<script>
+import { Toast } from 'vant'
+
+export default {
+    data() {
+        return {
+            loginForm: {
+                username: '',
+                password: '',
+                validateCode: '',
+                captchaId: '',
+                captchaImage: '',
+                url: 'login_by_company',
+                checked: true
+            },
+            loading: false
+        }
+    },
+    created() {
+        this.loginForm.checked = localStorage.getItem('remember') === '1'
+        //if (this.loginForm.checked) {
+        this.loginForm.username = localStorage.getItem('username')
+        this.loginForm.password = localStorage.getItem('password')
+        //}
+    },
+    mounted() {
+        this.$store.dispatch('user/cleanCache')
+        this.captchaImageRefresh(this.loginForm)
+    },
+    methods: {
+        loginClick: function () {
+            this.loading = true
+            localStorage.setItem('remember', this.loginForm.checked ? '1' : '0')
+            localStorage.setItem('username', this.loginForm.username)
+            localStorage.setItem('password', this.loginForm.password)
+
+            this.$store
+                .dispatch('user/login1', this.loginForm)
+                .then(res => {
+                    if (res.result) {
+                        this.$router.push('/mobile-home')
+                        this.loading = false
+                        Toast('登录成功')
+                    } else {
+                        Toast(res.msg)
+                        this.loading = false
+                    }
+                })
+                .catch(err => {
+                    console.log(err)
+                    Toast(err)
+                    this.loading = false
+                })
+        },
+        captchaImageRefresh: function (_form) {
+            _form.captchaId = this.$common.uuid(8)
+            _form.captchaImage =
+                this.$constant.BASE_URI +
+                '/captcha/captchaImage?type=math&captchaId=' +
+                _form.captchaId +
+                '&s=' +
+                Math.random()
+        }
+    }
+}
+</script>
+<style scoped>
+.page-body {
+    height: 100vh;
+    width: 100vw;
+    background-image: url(../../assets/mobile-Page-Image/bg01.png);
+    background-size: 100%;
+    background-position: top;
+    background-repeat: no-repeat;
+}
+
+.client {
+    font-size: 1rem;
+    font-weight: 400;
+}
+.tips {
+    width: 60vw;
+    margin: 2vh auto;
+    padding: 0 20vw;
+    color: #cccccc;
+    font-size: 2vw;
+    position: absolute;
+    bottom: 0px;
+}
+
+.mmm:after {
+    border-bottom: none;
+}
+</style>

+ 67 - 0
src.mobile/views/mobile-page/mobile-main.vue

@@ -0,0 +1,67 @@
+<template>
+    <div style="height: 100vh; width: 100vw">
+        <van-nav-bar title="我的" @click-left="toHome">
+            <template #left>
+                <van-icon name="arrow-left" />
+            </template>
+        </van-nav-bar>
+        <div class="bg02"></div>
+        <van-row style="margin-top: 20%">
+            <van-col :span="18" :offset="3" style="border-radius: 10px; overflow: hidden">
+                <van-cell title="我的信息" @click="toMyInfo" is-link></van-cell>
+                <van-cell title="当前版本号" value="V1.0"></van-cell>
+                <van-cell title="密码修改" @click="toChangePsw" is-link></van-cell>
+                <van-cell title="退出系统" @click="logout" is-link></van-cell>
+                <div style="height: 80px; width: 100%; background-color: white"></div>
+            </van-col>
+        </van-row>
+    </div>
+</template>
+<script>
+import { Toast } from 'vant'
+
+export default {
+    data() {
+        return {}
+    },
+    methods: {
+        toHome() {
+            this.$router.push('/mobile-home')
+        },
+        toMyInfo() {
+            this.$router.push('/mobile-myInfor')
+        },
+        toChangePsw() {
+            this.$router.push('/mobile-changePsw')
+        },
+        logout() {
+            this.$store.dispatch('user/cleanCache').then(res => {
+                Toast('退出成功')
+                this.$router.push('/mobile-login')
+            })
+        }
+    }
+}
+</script>
+<style scoped>
+.bg02 {
+    background: url(../../assets/mobile-Page-Image/bg01.png) no-repeat;
+    width: 100vw;
+    height: 100vh;
+    background-size: 100%;
+    position: absolute;
+}
+
+::v-deep .van-cell {
+    height: 60px;
+    line-height: 60px;
+}
+::v-deep .van-cell__title {
+    height: 60px;
+    line-height: 60px;
+}
+::v-deep .van-cell__right-icon {
+    height: 60px;
+    line-height: 60px;
+}
+</style>

+ 59 - 0
src.mobile/views/mobile-page/mobile-myInfor.vue

@@ -0,0 +1,59 @@
+<template>
+    <div style="height: 100vh; width: 100vw">
+        <van-nav-bar title="我的" @click-left="toMain">
+            <template #left>
+                <van-icon name="arrow-left" />
+            </template>
+        </van-nav-bar>
+        <van-row style="margin-top: 10%; z-index: 99">
+            <van-col :span="22" :offset="1" style="border-radius: 10px; overflow: hidden">
+                <van-cell title="企业名称:" :value="companyInfo.qymc"></van-cell>
+                <van-cell title="企业联系人:" :value="companyInfo.qylxr"></van-cell>
+                <div
+                    style="height: 20px; width: 100%; background-color: white; z-index: 9999"
+                ></div>
+            </van-col>
+        </van-row>
+    </div>
+</template>
+<script>
+import { Toast } from 'vant'
+
+export default {
+    data() {
+        return {
+            companyInfo: {
+                qymc: '',
+                qylxr: ''
+            }
+        }
+    },
+    created() {
+        let currUser = this.$common.currUser()
+        if (currUser) {
+            this.companyInfo.qymc = currUser.qymc
+            this.companyInfo.qylxr = currUser.qylxr
+        }
+    },
+    mounted() {},
+    methods: {
+        toMain() {
+            this.$router.push('/mobile-main')
+        }
+    }
+}
+</script>
+<style scoped>
+::v-deep .van-cell {
+    height: 60px;
+    line-height: 60px;
+}
+::v-deep .van-cell__title {
+    height: 60px;
+    line-height: 60px;
+}
+::v-deep .van-cell__right-icon {
+    height: 60px;
+    line-height: 60px;
+}
+</style>

+ 43 - 0
src.mobile/views/report.vue

@@ -0,0 +1,43 @@
+<template>
+    <div>
+        <van-nav-bar
+            style="position: fixed; top: 0; width: 100%"
+            :title="title"
+            left-arrow
+            @click-left="onClickLeft"
+        />
+        <keep-alive>
+            <router-view v-if="$route.meta.keepAlive" style="margin-top: 45px"></router-view>
+        </keep-alive>
+        <router-view v-if="!$route.meta.keepAlive" style="margin-top: 45px"></router-view>
+    </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+
+export default {
+    name: 'Report',
+    data() {
+        return {}
+    },
+    computed: {
+        ...mapGetters(['title'])
+    },
+    watch: {},
+    mounted() {
+        this.$store.dispatch('layout/init', {})
+    },
+    methods: {
+        onClickLeft: function () {
+            this.$store.dispatch('layout/pre', {}).then(res => {
+                if (res < 0) {
+                    this.$router.push('/')
+                } else {
+                    this.$router.push('/report')
+                }
+            })
+        }
+    }
+}
+</script>

+ 34 - 0
src.mobile/views/reportDetail.vue

@@ -0,0 +1,34 @@
+<template>
+    <div>
+        <fire-table :query-type="type" :all-columns="columns"></fire-table>
+    </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import FireTable from './FireTable'
+
+export default {
+    name: 'ReportDetail',
+    components: { FireTable },
+    data() {
+        return {
+            type: '1',
+            columns: true
+        }
+    },
+    computed: {
+        ...mapGetters(['gValue'])
+    },
+    watch: {},
+    mounted() {
+        if (!this.gValue) {
+            this.$router.push('/report')
+            return
+        }
+        this.$store.dispatch('layout/next', {})
+        this.type = this.$route.query.type
+    },
+    methods: {}
+}
+</script>

+ 163 - 0
src.mobile/views/reportMain.vue

@@ -0,0 +1,163 @@
+<template>
+    <div style="margin: 5px">
+        <van-search
+            v-model="value"
+            input-align="center"
+            left-icon=""
+            readonly
+            right-icon="arrow-down"
+            @click="showPicker = true"
+        />
+        <van-popup v-model="showPicker" round position="bottom">
+            <van-picker
+                show-toolbar
+                :columns="columns"
+                @cancel="showPicker = false"
+                @confirm="onConfirm"
+            />
+        </van-popup>
+        <van-grid column-num="3" style="margin: 10px 0; font-size: 14px; color: #aaa">
+            <van-grid-item :to="{ path: '/reportDetail', query: { type: 1 } }">
+                <div>
+                    <div>应到人数</div>
+                    <div class="tt">{{ stat.yd }}</div>
+                </div>
+            </van-grid-item>
+            <van-grid-item :to="{ path: '/reportDetail', query: { type: 2 } }">
+                <div>
+                    <div>签到人数</div>
+                    <div class="tt">{{ stat.qd }}</div>
+                </div>
+            </van-grid-item>
+            <van-grid-item :to="{ path: '/reportDetail', query: { type: 3 } }">
+                <div>
+                    <div>未到人数</div>
+                    <div class="tt">{{ stat.wd }}</div>
+                </div>
+            </van-grid-item>
+            <van-grid-item :to="{ path: '/reportDetail', query: { type: 4 } }">
+                <div>
+                    <div class="tt1">员工(应到/未到)</div>
+                    <div class="tt">
+                        {{ stat.yg }}/<span style="color: red">{{ stat.yg1 }}</span>
+                    </div>
+                </div>
+            </van-grid-item>
+            <van-grid-item :to="{ path: '/reportDetail', query: { type: 5 } }">
+                <div>
+                    <div class="tt1">第三方人员(应到/未到)</div>
+                    <div class="tt">
+                        {{ stat.dsfry }}/<span style="color: red">{{ stat.dsfry1 }}</span>
+                    </div>
+                </div>
+            </van-grid-item>
+            <van-grid-item :to="{ path: '/reportDetail', query: { type: 6 } }">
+                <div>
+                    <div class="tt1">访客(应到/未到)</div>
+                    <div class="tt">
+                        {{ stat.fk }}/<span style="color: red">{{ stat.fk1 }}</span>
+                    </div>
+                </div>
+            </van-grid-item>
+        </van-grid>
+        <van-tabs v-model="active">
+            <van-tab name="tab1" title="未到详情">
+                <fire-table :query-type="7" :all-columns="false"></fire-table>
+            </van-tab>
+            <van-tab name="tab2" title="已到详情">
+                <fire-table :query-type="8" :all-columns="true"></fire-table>
+            </van-tab>
+        </van-tabs>
+    </div>
+</template>
+
+<script>
+import FireTable from './FireTable'
+import { mapGetters } from 'vuex'
+
+export default {
+    name: 'ReportMain',
+    components: { FireTable },
+    data() {
+        return {
+            value: '',
+            showPicker: false,
+            columns: [],
+            active: 'tab1',
+            interval: null,
+            stat: {
+                yd: '',
+                qd: '',
+                wd: '',
+                yg: '',
+                yg1: '',
+                dsfry: '',
+                dsfry1: '',
+                fk: '',
+                fk1: ''
+            }
+        }
+    },
+    computed: {
+        ...mapGetters(['gValue'])
+    },
+    mounted() {
+        this.$store.dispatch('layout/gValue', null)
+        this.querySelect()
+    },
+    methods: {
+        onConfirm(value) {
+            this.$store.dispatch('layout/gValue', value.value)
+            this.value = value.text
+            this.showPicker = false
+            this.queryStat()
+        },
+        queryStat() {
+            const postData = {
+                yxid: this.gValue
+            }
+            this.$apis.requestBase('stat', postData).then(res => {
+                this.stat = res.data
+            })
+        },
+        querySelect() {
+            if (this.columns && this.columns.length > 0) {
+                return
+            }
+            const postData = {}
+            this.$apis.requestBase('select', postData).then(res => {
+                this.columns = res.data
+                if (this.columns[0]) {
+                    this.onConfirm(this.columns[0])
+                }
+                const _this = this
+                // 30s刷新界面数据
+                if (this.interval) {
+                    clearInterval(this.interval)
+                }
+                this.interval = setInterval(() => {
+                    _this.queryStat()
+                }, 30000)
+            })
+        }
+    },
+    destroyed() {
+        if (this.interval) {
+            clearInterval(this.interval)
+        }
+    }
+}
+</script>
+
+<style>
+.tt {
+    text-align: center;
+    color: #555;
+    height: 40px;
+    line-height: 40px;
+}
+
+.tt1 {
+    font-size: 10px;
+}
+</style>

+ 323 - 0
src.mobile/views/sign.vue

@@ -0,0 +1,323 @@
+<template>
+    <div>
+        <van-nav-bar
+            title="打卡签到"
+            style="position: fixed; top: 0; width: 100%"
+            left-arrow
+            @click-left="onClickLeft"
+        />
+
+        <van-tabs v-model="active" style="margin-top: 45px" @change="changeTab">
+            <van-tab name="tab1" title="新增签到">
+                <div style="margin: 5px; height: 500px">
+                    <van-field
+                        style="line-height: 60px; font-size: 18px"
+                        v-model="value"
+                        label="应急事件"
+                        clickable
+                        readonly
+                        @click="selectClick(1)"
+                        right-icon="arrow-down"
+                    />
+                    <van-popup v-model="showPicker" round position="bottom">
+                        <van-search
+                            v-if="searchFilterShow"
+                            v-model="searchFilter"
+                            input-align="center"
+                            show-action
+                            placeholder="请输入关键字"
+                            @search="onSearchFilter"
+                        >
+                            <template #action>
+                                <div @click="onSearchFilter">搜索</div>
+                            </template>
+                        </van-search>
+                        <van-picker
+                            show-toolbar
+                            :columns="columnsFilter"
+                            @cancel="showPicker = false"
+                            @confirm="onConfirm"
+                        />
+                    </van-popup>
+
+                    <van-field
+                        v-model="value1"
+                        label="签到点"
+                        style="line-height: 40px; font-size: 16px"
+                        required
+                        clickable
+                        readonly
+                        @click="selectClick(2)"
+                        right-icon="arrow-down"
+                    />
+                    <van-field
+                        v-model="value2"
+                        label="签到人"
+                        style="line-height: 40px; font-size: 16px"
+                        required
+                        clickable
+                        readonly
+                        @click="selectClick(3)"
+                        right-icon="arrow-down"
+                    />
+                </div>
+                <van-row
+                    type="flex"
+                    justify="center"
+                    style="width: 100%; background: white; position: fixed; bottom: 20px"
+                >
+                    <van-col span="5">
+                        <van-button type="default" @click="onClickLeft">关闭</van-button>
+                    </van-col>
+                    <van-col span="5">
+                        <van-button color="#B2568C" type="primary" @click="submitData">
+                            提交
+                        </van-button>
+                    </van-col>
+                </van-row>
+            </van-tab>
+            <van-tab name="tab2" title="我的签到">
+                <div style="margin: 5px">
+                    <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
+                        <van-list
+                            v-model="loading"
+                            :finished="finished"
+                            finished-text="没有更多了"
+                            @load="onLoad"
+                        >
+                            <van-cell v-for="item in list" :key="item">
+                                <template #title>
+                                    <van-row>
+                                        <van-col span="4"> 应急事件: </van-col>
+                                        <van-col span="20">&nbsp;&nbsp;{{ item.yjsj }}</van-col>
+                                    </van-row>
+                                    <van-row>
+                                        <van-col span="4">签&nbsp;&nbsp;到&nbsp;&nbsp;点:</van-col>
+                                        <van-col span="20">&nbsp;&nbsp;{{ item.qdd }}</van-col>
+                                    </van-row>
+                                    <van-row>
+                                        <van-col span="4">签&nbsp;&nbsp;到&nbsp;&nbsp;人:</van-col>
+                                        <van-col span="6">&nbsp;&nbsp;{{ item.qdr }}</van-col>
+                                        <van-col span="4">签到时间:</van-col>
+                                        <van-col span="10">&nbsp;&nbsp;{{ item.qdsj }}</van-col>
+                                    </van-row>
+                                </template>
+                            </van-cell>
+                        </van-list>
+                    </van-pull-refresh>
+                </div>
+            </van-tab>
+        </van-tabs>
+    </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import { Toast } from 'vant'
+import cookies from '../plugin/axios/util.cookies'
+
+export default {
+    name: 'Sign',
+    data() {
+        return {
+            searchFilter: '',
+            searchFilterShow: false,
+            value: '',
+            value1: '',
+            value1Key: '',
+            value2: '',
+            value2Key: '',
+            value2Type: '',
+            page: 1,
+            active: 'tab1',
+
+            showPicker: false,
+            showType: 1,
+            columns: [],
+            columnsFilter: [],
+            yjs: [],
+            qdds: [],
+            qdrs: [],
+
+            list: [],
+            loading: false,
+            finished: false,
+            refreshing: false
+        }
+    },
+    computed: {
+        ...mapGetters(['gValue'])
+    },
+    watch: {
+        gValue: function (n, o) {
+            if (n) {
+                this.querySelect()
+            }
+        }
+    },
+    mounted() {
+        this.$store.dispatch('layout/gValue', null)
+        this.querySelect()
+    },
+    methods: {
+        onSearchFilter() {
+            if (this.searchFilter) {
+                this.columnsFilter = []
+                const _this = this
+                this.columns.forEach(v => {
+                    if (v.text.indexOf(_this.searchFilter) > -1) {
+                        _this.columnsFilter.push(v)
+                    }
+                })
+            } else {
+                this.columnsFilter = this.columns
+            }
+        },
+        changeTab(name) {
+            if (name === 'tab2') {
+                this.finished = false
+                this.loading = true
+                this.onLoad(1)
+            }
+        },
+        submitData() {
+            const postData = {
+                yxid: this.gValue,
+                qdd: this.value1Key,
+                qdr: this.value2Key,
+                qdrtype: this.value2Type
+            }
+            this.$apis.requestBase('sign', postData).then(res => {
+                if (res.data && res.data.code === 200) {
+                    this.value2 = ''
+                    this.value2Key = ''
+                    this.value2Type = ''
+                    this.querySelect()
+                    Toast('签到成功')
+                } else {
+                    Toast(res.data.msg)
+                }
+            })
+        },
+        selectClick(type) {
+            this.searchFilter = ''
+            if (type === 1) {
+                this.searchFilterShow = false
+                this.columns = this.yjs
+                this.columnsFilter = this.yjs
+            } else if (type === 2) {
+                this.searchFilterShow = false
+                this.columns = this.qdds
+                this.columnsFilter = this.qdds
+            } else if (type === 3) {
+                this.searchFilterShow = true
+                this.columns = this.qdrs
+                this.columnsFilter = this.qdrs
+            }
+            this.showType = type
+            this.showPicker = true
+        },
+        querySelect() {
+            let tmp = this.gValue
+            if (!this.yjs || this.yjs.length <= 0) {
+                tmp = ''
+            }
+            const postData = {
+                yxid: tmp
+            }
+            this.$apis.requestBase('selectAll', postData).then(res => {
+                if (res.data.qdds) {
+                    this.qdds = res.data.qdds
+                    const SignValue1Key = cookies.get('SignValue1Key')
+                    if (SignValue1Key) {
+                        this.qdds.forEach(v => {
+                            if (v.value === SignValue1Key) {
+                                this.value1 = v.text
+                                this.value1Key = v.value
+                            }
+                        })
+                    }
+                }
+                if (res.data.qdrs) {
+                    this.qdrs = res.data.qdrs
+                }
+                if (res.data.yjs) {
+                    this.yjs = res.data.yjs
+                    if (this.yjs[0]) {
+                        this.onConfirm(this.yjs[0])
+                    }
+                }
+            })
+        },
+        onClickLeft: function () {
+            this.$router.push('/')
+        },
+        onConfirm(value) {
+            this.searchFilter = ''
+            if (this.showType === 1) {
+                this.$store.dispatch('layout/gValue', value.value)
+                this.value = value.text
+            } else if (this.showType === 2) {
+                cookies.set('SignValue1Key', value.value)
+                this.value1 = value.text
+                this.value1Key = value.value
+            } else if (this.showType === 3) {
+                this.value2 = value.text
+                this.value2Key = value.value
+                this.value2Type = value.type
+            }
+            this.showPicker = false
+        },
+        onLoad(page) {
+            // if (!this.gValue) {
+            //     this.finished = true
+            //     this.loading = false
+            //     return
+            // }
+            if (page) {
+                this.page = page
+            }
+            page = this.page
+            this.page += 1
+
+            // 查询类型 queryType
+            // 演习id=gValue
+            // 排序情况= columns . sort
+            // 搜索情况 this.value
+            const postData = {
+                // yxid: this.gValue,
+                page: page
+            }
+
+            this.$apis.requestBase('mysign', postData).then(res => {
+                if (res.data && res.data.length > 0) {
+                    if (page === 1) {
+                        this.list = res.data
+                    } else {
+                        this.list.push(res.data)
+                    }
+                } else {
+                    if (page === 1) {
+                        this.list = []
+                    }
+                    this.finished = true
+                }
+                this.loading = false
+                if (this.refreshing) {
+                    this.refreshing = false
+                }
+            })
+        },
+        onRefresh() {
+            this.finished = false
+            this.loading = true
+            this.onLoad(1)
+        }
+    }
+}
+</script>
+<style lang="scss">
+.van-picker-column__item--selected {
+    background-color: RGBA(178, 86, 140, 0.3);
+}
+</style>

+ 11 - 0
src/App.vue

@@ -0,0 +1,11 @@
+<template>
+  <div id="app">
+    <router-view />
+  </div>
+</template>
+
+<script>
+export default {
+    name: 'App'
+}
+</script>

+ 52 - 0
src/api/user.js

@@ -0,0 +1,52 @@
+import channel from '../static/utils/channel'
+
+const users = {
+    'admin-token': {
+        perms: ['admin'],
+        introduction: 'I am a super administrator',
+        avatar: require('../static/images/header.gif'),
+        name: 'Super Admin'
+        // perms: ['GET /admin/admin/list', 'GET /admin/role/list']
+    },
+    'editor-token': {
+        perms: ['editor'],
+        introduction: 'I am an editor',
+        avatar: require('../static/images/header.gif'),
+        name: 'Normal Editor'
+        // perms: []
+    }
+}
+
+export function getInfo(token) {
+    return new Promise((resolve, reject) => {
+        const info = users[token]
+        if (!info) {
+            const rej = {
+                code: 50008,
+                message: 'Login failed, unable to get user details.'
+            }
+            reject(rej)
+        } else {
+            const res = {
+                code: 20000,
+                data: info
+            }
+            resolve(res)
+            // debugger
+        }
+    })
+}
+
+export function logout() {
+    return new Promise((resolve, reject) => {
+        channel.baseRequest('web', 'logout', '', 'logout').then((res) => {
+            if (res) {
+                resolve(res)
+            }
+            reject()
+        }).catch((err, x) => {
+            reject(err)
+            console.log('api/user/logout', err, x)
+        })
+    })
+}

binární
src/assets/404_images/404.png


binární
src/assets/404_images/404_cloud.png


binární
src/assets/mobile-Page-Image/454.png


binární
src/assets/mobile-Page-Image/bg01.png


binární
src/assets/mobile-Page-Image/bg02.png


binární
src/assets/mobile-Page-Image/right2x.png


binární
src/assets/mobile-Page-Image/矩形 186.png


binární
src/assets/mobile-Page-Image/组 454.png


binární
src/assets/mobile-Page-Image/组 461.png


binární
src/assets/mobile-Page-Image/组 462.png


binární
src/assets/mobile-Page-Image/首页_slices/127.png


binární
src/assets/mobile-Page-Image/首页_slices/134.png


binární
src/assets/mobile-Page-Image/首页_slices/ddc79472428947.5be942464d407@2x.png


binární
src/assets/mobile-Page-Image/首页_slices/组 127.png


binární
src/assets/mobile-Page-Image/首页_slices/组 134.png


binární
src/assets/mobile-Page-Image/首页_slices/组 459.png


binární
src/assets/mobile-Page-Image/首页_slices/组 460.png


binární
src/assets/mobile-Page-Image/首页_slices/蒙版组 2.png


binární
src/assets/newLogin-image/Desktop/组 464.png


binární
src/assets/newLogin-image/Desktop/组 466.png


binární
src/assets/newLogin-image/Desktop/组 467.png


binární
src/assets/newLogin-image/QR2X.png


+ 0 - 0
src/assets/newLogin-image/qrcode.png


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů