lrf402788946 5 vuotta sitten
commit
5b20777111
93 muutettua tiedostoa jossa 10131 lisäystä ja 0 poistoa
  1. 1 0
      .env
  2. 33 0
      .eslintrc.js
  3. 22 0
      .gitignore
  4. 29 0
      README.md
  5. 3 0
      babel.config.js
  6. 60 0
      package.json
  7. BIN
      public/favicon.ico
  8. 17 0
      public/index.html
  9. 21 0
      src/App.vue
  10. BIN
      src/assets/default_logo.png
  11. BIN
      src/assets/homejianli.png
  12. BIN
      src/assets/jianlitupian.png
  13. BIN
      src/assets/logo.png
  14. BIN
      src/assets/schoolLogo.png
  15. BIN
      src/assets/schoolLogo1.png
  16. BIN
      src/assets/schoolLogo2.png
  17. BIN
      src/assets/schoolLogo3.png
  18. 53 0
      src/components/back-bar.vue
  19. 90 0
      src/components/bind.vue
  20. 84 0
      src/components/form-item.vue
  21. 80 0
      src/components/news-form.vue
  22. 54 0
      src/components/qrcode.vue
  23. 50 0
      src/components/search.vue
  24. 230 0
      src/components/tag-all-select.vue
  25. 137 0
      src/components/tag-checkbox.vue
  26. 204 0
      src/components/tag-sec-select.vue
  27. 152 0
      src/components/upload-file.vue
  28. 75 0
      src/components/upload.vue
  29. 77 0
      src/components/wang-editor.vue
  30. 37 0
      src/layout/detail-layout.vue
  31. 226 0
      src/layout/layout-part/heads.vue
  32. 81 0
      src/layout/layout-part/menus.vue
  33. 57 0
      src/layout/list-normal.vue
  34. 57 0
      src/layout/list-tab.vue
  35. 74 0
      src/layout/main-layout.vue
  36. 23 0
      src/main.js
  37. 19 0
      src/plugins/axios.js
  38. 39 0
      src/plugins/check-res.js
  39. 5 0
      src/plugins/element.js
  40. 6 0
      src/plugins/filters.js
  41. 27 0
      src/plugins/loading.js
  42. 4 0
      src/plugins/meta.js
  43. 33 0
      src/plugins/methods.js
  44. 20 0
      src/plugins/setting.js
  45. 65 0
      src/plugins/stomp.js
  46. 10 0
      src/plugins/var.js
  47. 123 0
      src/router.js
  48. 333 0
      src/store.js
  49. 11 0
      src/style/element.less
  50. 132 0
      src/util/axios-wrapper.js
  51. 10 0
      src/util/filters.js
  52. 50 0
      src/util/methods-util.js
  53. 47 0
      src/util/optionTitles.js
  54. 96 0
      src/util/qrcode.vue
  55. 69 0
      src/util/user-util.js
  56. 367 0
      src/views/home/index.vue
  57. 196 0
      src/views/info/base/auth.vue
  58. 72 0
      src/views/info/base/index.vue
  59. 154 0
      src/views/info/base/info.vue
  60. 0 0
      src/views/info/bind/index.vue
  61. 241 0
      src/views/info/hr/index.vue
  62. 487 0
      src/views/jobs/jobfair/detail.vue
  63. 164 0
      src/views/jobs/jobfair/index.vue
  64. 103 0
      src/views/jobs/jobfair/info.vue
  65. 127 0
      src/views/jobs/jobfair/list.vue
  66. 291 0
      src/views/jobs/jobinfo/detail.vue
  67. 217 0
      src/views/jobs/jobinfo/index.vue
  68. 118 0
      src/views/jobs/profile/detail.vue
  69. 185 0
      src/views/jobs/profile/index.vue
  70. 472 0
      src/views/jobs/school/detail.vue
  71. 259 0
      src/views/jobs/school/index.vue
  72. 428 0
      src/views/jobs/talk/detail.vue
  73. 200 0
      src/views/jobs/talk/index.vue
  74. 99 0
      src/views/manager/deliver/index.vue
  75. 127 0
      src/views/manager/deliver/list.vue
  76. 161 0
      src/views/manager/invite/index.vue
  77. 382 0
      src/views/manager/jobs/detail.vue
  78. 172 0
      src/views/manager/jobs/index.vue
  79. 69 0
      src/views/manager/jobs/resume.vue
  80. 194 0
      src/views/manager/jobs/resume/context.vue
  81. 199 0
      src/views/manager/jobs/resume/resume-tem.vue
  82. 418 0
      src/views/manager/plan/detail.vue
  83. 155 0
      src/views/manager/plan/index.vue
  84. 100 0
      src/views/manager/plan/interview-detail.vue
  85. 96 0
      src/views/manager/plan/notify-record.vue
  86. 143 0
      src/views/manager/record/index.vue
  87. 86 0
      src/views/manager/resume/index.vue
  88. 103 0
      src/views/manager/resume/list.vue
  89. 42 0
      src/views/test/test-detail.vue
  90. 142 0
      src/views/test/test-list-normal.vue
  91. 92 0
      src/views/test/test-list-tab.vue
  92. 101 0
      src/views/test/test-list-tab/list.vue
  93. 43 0
      vue.config.js

+ 1 - 0
.env

@@ -0,0 +1 @@
+VUE_APP_AXIOS_BASE_URL = ''

+ 33 - 0
.eslintrc.js

@@ -0,0 +1,33 @@
+// https://eslint.org/docs/user-guide/configuring
+
+module.exports = {
+  root: true,
+  env: {
+    node: true,
+  },
+  extends: ['plugin:vue/essential', '@vue/prettier'],
+  plugins: ['vue'],
+  rules: {
+    'max-len': [
+      'warn',
+      {
+        code: 250,
+      },
+    ],
+    'no-unused-vars': 'off',
+    'no-console': 'off',
+    'prettier/prettier': [
+      'warn',
+      {
+        singleQuote: true,
+        trailingComma: 'es5',
+        bracketSpacing: true,
+        jsxBracketSameLine: true,
+        printWidth: 160,
+      },
+    ],
+  },
+  parserOptions: {
+    parser: 'babel-eslint',
+  },
+};

+ 22 - 0
.gitignore

@@ -0,0 +1,22 @@
+.DS_Store
+node_modules
+/dist
+package-lock.json
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 29 - 0
README.md

@@ -0,0 +1,29 @@
+# web-bench
+
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Run your tests
+```
+npm run test
+```
+
+### Lints and fixes files
+```
+npm run lint
+```
+
+### Customize configuration
+See [Configuration Reference](https://cli.vuejs.org/config/).

+ 3 - 0
babel.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  presets: ['@vue/app'],
+};

+ 60 - 0
package.json

@@ -0,0 +1,60 @@
+{
+  "name": "web-bench",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "@stomp/stompjs": "^5.4.2",
+    "axios": "^0.19.0",
+    "core-js": "^2.6.5",
+    "element-ui": "^2.11.1",
+    "jsonwebtoken": "^8.5.1",
+    "lodash": "^4.17.15",
+    "naf-core": "^0.1.2",
+    "qrcode": "^1.4.1",
+    "vue": "^2.6.10",
+    "vue-meta": "^2.2.1",
+    "vue-router": "^3.1.1",
+    "vuex": "^3.0.1",
+    "wangeditor": "^3.1.1"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "^3.9.0",
+    "@vue/cli-plugin-eslint": "^3.9.0",
+    "@vue/cli-service": "^3.9.0",
+    "@vue/eslint-config-prettier": "^4.0.1",
+    "babel-eslint": "^10.0.1",
+    "eslint": "^5.16.0",
+    "eslint-plugin-vue": "^5.0.0",
+    "less": "^3.0.4",
+    "less-loader": "^4.1.0",
+    "vue-template-compiler": "^2.6.10"
+  },
+  "eslintConfig": {
+    "root": true,
+    "env": {
+      "node": true
+    },
+    "extends": [
+      "plugin:vue/essential",
+      "@vue/prettier"
+    ],
+    "rules": {},
+    "parserOptions": {
+      "parser": "babel-eslint"
+    }
+  },
+  "postcss": {
+    "plugins": {
+      "autoprefixer": {}
+    }
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions"
+  ]
+}

BIN
public/favicon.ico


+ 17 - 0
public/index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title>web-bench</title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but web-bench doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 21 - 0
src/App.vue

@@ -0,0 +1,21 @@
+<template>
+  <div id="app">
+    <main-layout></main-layout>
+  </div>
+</template>
+<script>
+import mainLayout from '@/layout/main-layout.vue';
+export default {
+  name: 'app',
+  components: {
+    mainLayout,
+  },
+};
+</script>
+
+<style lang="less">
+body {
+  margin: 0;
+  padding: 0;
+}
+</style>

BIN
src/assets/default_logo.png


BIN
src/assets/homejianli.png


BIN
src/assets/jianlitupian.png


BIN
src/assets/logo.png


BIN
src/assets/schoolLogo.png


BIN
src/assets/schoolLogo1.png


BIN
src/assets/schoolLogo2.png


BIN
src/assets/schoolLogo3.png


+ 53 - 0
src/components/back-bar.vue

@@ -0,0 +1,53 @@
+<template>
+  <div id="back-bar">
+    <el-row class="detailReturn">
+      <el-button @click="$router.push(to)"><i class="el-icon-arrow-left"></i></el-button>
+      <el-col class="detailTit">{{ title }}</el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'back-bar',
+  props: {
+    to: { type: String, default: '/' },
+    title: { type: String, default: '' },
+  },
+  components: {},
+  data: () => ({}),
+  created() {},
+  computed: {},
+  methods: {},
+};
+</script>
+
+<style lang="less" scoped>
+.detailReturn .el-button {
+  float: left;
+  width: 26px;
+  height: 26px;
+  border-radius: 90px;
+  padding: 0;
+  border: 2px solid #9e9fa1;
+  margin: 0 5px 0 0;
+}
+.detailReturn .el-button:hover {
+  background: #fff;
+}
+.detailReturn .el-button:hover i {
+  color: #333333;
+}
+.detailReturn .el-button i {
+  font-size: 15px;
+  font-weight: bold;
+}
+.detailTit {
+  float: left;
+  width: 96%;
+  height: 26px;
+  line-height: 26px;
+  font-size: 14px;
+  color: #333333;
+}
+</style>

+ 90 - 0
src/components/bind.vue

@@ -0,0 +1,90 @@
+<template>
+  <div id="bind">
+    <el-dialog title="绑定微信" :visible.sync="dialog" width="40%" center :close-on-click-modal="false" :close-on-press-escape="false" :show-close="false">
+      <el-row>
+        <slot></slot>
+        <el-col :span="24" style="text-align:center">
+          <qrcode :qrcode="qrcode" @toReturn="toReturn"></qrcode>
+        </el-col>
+        <slot name="footer"></slot>
+      </el-row>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+const jwt = require('jsonwebtoken');
+import { mapActions, mapState, mapMutations } from 'vuex';
+import qrcode from '@/components/qrcode.vue';
+export default {
+  name: 'bind',
+  props: {
+    display: { type: Boolean },
+    mobile: null,
+  },
+  components: {
+    qrcode,
+  },
+  data: () => ({
+    dialog: false,
+    qrcode: '',
+  }),
+  watch: {
+    display: {
+      handler(val) {
+        this.$set(this, `dialog`, val);
+      },
+    },
+  },
+  created() {
+    this.initQrcode();
+  },
+  mounted() {},
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapMutations(['setUser']),
+    ...mapActions(['createConnection', 'getWxtoken', 'bindOperation', 'userOperation']),
+    async initQrcode() {
+      let result = await this.createConnection();
+      this.$set(this, `qrcode`, result);
+    },
+    async toReturn(msg) {
+      let result = await this.getWxtoken(this.qrcode);
+      this.toBind(result);
+    },
+    async toBind(wxtoken) {
+      let object = { corpid: this.user.corpid, wxtoken: wxtoken };
+      if (!this.mobile) {
+        //是默认绑定
+        console.log('first login');
+        object['mobile'] = this.user.sub;
+      } else {
+        //hr绑定
+        console.log('hr');
+        object['mobile'] = this.mobile;
+      }
+      let result = await this.bindOperation({ data: object });
+      if (`${result.errcode}` === '0') {
+        this.$message.success('操作成功');
+        if (!this.mobile) this.wxLogin(wxtoken);
+        else this.$emit(`close`, 'bind');
+      } else {
+        this.$message.error(result.errmsg);
+      }
+    },
+    async wxLogin(wxtoken) {
+      let result = await this.userOperation({ type: 'login', data: { loginType: '1', info: { wxtoken: wxtoken } } });
+      if (`${result.errcode}` === '0') {
+        let info = jwt.decode(result.data);
+        sessionStorage.setItem('user', JSON.stringify(info));
+        this.setUser(info);
+        window.location.href = `${window.location.href}`;
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 84 - 0
src/components/form-item.vue

@@ -0,0 +1,84 @@
+<template>
+  <div id="form-item">
+    <!-- <el-row class="input_row" v-if="type === 'text'">
+      <el-form-item :prop="prop">
+        <el-col :span="4">{{ label }}</el-col>
+        <el-col :span="20">
+          <el-popover :disabled="!usePop" placement="top-start" trigger="hover" ref="input">
+            <el-alert :closable="false" :title="popContent" type="warning" effect="dark"> </el-alert>
+          </el-popover>
+          <el-input :value="modelVal" :disabled="disabled" :placeholder="placeholder" @input="handleInput" v-popover:input></el-input>
+        </el-col>
+      </el-form-item>
+    </el-row> -->
+    <el-row class="input_row">
+      <el-form-item :prop="prop">
+        <el-col :span="4">
+          <slot name="label">{{ label }}</slot>
+        </el-col>
+        <el-col :span="20">
+          <slot></slot>
+        </el-col>
+      </el-form-item>
+    </el-row>
+    <!-- <el-row class="input_row" v-if="type === 'select'">
+      <el-form-item :prop="prop">
+        <el-col :span="4">
+          {{ label }}
+        </el-col>
+        <el-col :span="20">
+          <el-select v-model="value" filterable :placeholder="placeholder" @input="handleInput">
+            <el-option v-for="(item, index) in list" :key="index" :label="item.label" :value="item.label"></el-option>
+          </el-select>
+        </el-col>
+      </el-form-item>
+    </el-row> -->
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'form-item',
+  //使用v-model方式的一种,此处为此方式的前置
+  model: {
+    prop: 'modelVal', //声明v-model变量过来用什么字符串接收
+    event: 'change', //v-model改变时,应该走这个名称的方法
+  },
+  props: {
+    modelVal: String, //给v-model开空间
+    type: { type: String, default: 'text' },
+    prop: { type: String, default: '' },
+    label: { type: String, default: '' },
+    disabled: { type: Boolean, default: false },
+    placeholder: { type: String, default: '请输入' },
+    list: { type: Array, default: () => [] },
+    usePop: { type: Boolean, default: false },
+    popContent: { type: String, default: '' },
+  },
+  components: {},
+  data: () => ({
+    value: '',
+    values: [],
+  }),
+  created() {},
+  computed: {},
+  mounted() {
+    if (this.modelVal) {
+      this.$set(this, `value`, this.modelVal);
+    }
+  },
+  methods: {
+    handleInput($event) {
+      //目前只试了text的情况
+      this.$emit('change', $event);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.input_row {
+  padding: 0 3rem;
+  margin-top: 0.6rem;
+}
+</style>

+ 80 - 0
src/components/news-form.vue

@@ -0,0 +1,80 @@
+<template>
+  <el-form ref="form" :model="form" :rules="rules" size="mini" label-width="100px">
+    <slot>
+      <el-form-item label="标题" prop="title" :required="true">
+        <el-input v-model="form.title"></el-input>
+      </el-form-item>
+      <el-form-item label="发布单位" prop="issuer" :required="true">
+        <el-input v-model="form.issuer"></el-input>
+      </el-form-item>
+      <el-form-item label="内容" prop="content" :required="true">
+        <wang-editor v-model="form.content"></wang-editor>
+      </el-form-item>
+      <el-form-item label="附件" prop="attachment">
+        <file-upload v-model="form.attachment"></file-upload>
+      </el-form-item>
+      <el-form-item label="标签" prop="tags">
+        <tags v-model="form.tags" add-text="添加标签"></tags>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="$emit('cancel')" size="mini">取 消</el-button>
+        <el-button type="primary" @click="handleSave" size="mini">保存</el-button>
+      </el-form-item>
+    </slot>
+  </el-form>
+</template>
+<script>
+import _ from 'lodash';
+import { createNamespacedHelpers } from 'vuex';
+import WangEditor from './editor/wang-editor';
+import FileUpload from './editor/file-upload';
+import Tags from './editor/tags';
+
+const requiredAndMaxlen = (name, len = 0) => {
+  const rules = [{ required: true, message: `${name}不能为空`, trigger: 'blur' }];
+  if (len > 0) rules.push({ max: len, message: `长度不能大于${len}个字符`, trigger: 'blur' });
+  return rules;
+};
+
+const { mapActions } = createNamespacedHelpers('naf/dict');
+
+export default {
+  components: {
+    WangEditor,
+    FileUpload,
+    Tags,
+  },
+  name: 'news-form',
+  props: {
+    data: { type: Object, required: true },
+    isNew: { type: Boolean, default: false } /* 是否新创建 */,
+  },
+  data() {
+    return {
+      form: _.cloneDeep(this.data),
+      rules: {
+        title: requiredAndMaxlen('标题', 100),
+        issuer: requiredAndMaxlen('发布单位', 64),
+        content: requiredAndMaxlen('内容', 102400),
+      },
+      units: [],
+    };
+  },
+  methods: {
+    handleSave({ action }) {
+      this.$refs['form'].validate(valid => {
+        if (valid) {
+          this.$emit('save', { isNew: this.isNew, data: this.form, action });
+        } else {
+          console.warn('form validate error!!!');
+        }
+      });
+    },
+  },
+};
+</script>
+<style lang="less" scoped>
+.el-form-item {
+  width: 700px;
+}
+</style>

+ 54 - 0
src/components/qrcode.vue

@@ -0,0 +1,54 @@
+<template>
+  <div id="qrcode">
+    <img :src="dataUrl" />
+  </div>
+</template>
+
+<script>
+import Vue from 'vue';
+import QRCode from 'qrcode';
+export default {
+  name: 'qrcode',
+  props: {
+    qrcode: null,
+  },
+  components: {},
+  data: () => ({
+    dataUrl: null,
+    token: null,
+  }),
+  async mounted() {
+    await this.initQrcode();
+  },
+  created() {},
+  computed: {},
+  methods: {
+    async initQrcode() {
+      // 创建二维码
+      if (!this.qrcode) return;
+      let uri = `${Vue.config.weixin.baseUrl}/qrcode/${this.qrcode}/scan`;
+      if (uri.startsWith('/')) {
+        uri = `${location.protocol}//${location.host}${uri}`;
+      }
+      this.dataUrl = await QRCode.toDataURL(uri);
+      this.$stomp({
+        [`/exchange/qrcode.login/${this.qrcode}`]: this.onMessage,
+      });
+    },
+    onMessage(message) {
+      console.log('receive a message: ', message.body);
+      if (message.body == 'scaned') {
+        try {
+          this.$emit('toReturn', message);
+          console.log('扫码登录成功');
+        } catch (err) {
+          console.log('扫码登录失败');
+          console.error(err);
+        }
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 50 - 0
src/components/search.vue

@@ -0,0 +1,50 @@
+<template>
+  <div id="search">
+    <div v-if="type === 'select'">
+      <el-select v-model="input" :placeholder="placeholder" filterable @input="handleInput">
+        <el-option v-for="(item, index) in newDataList" :key="index" :label="item.label" :value="item.value"></el-option>
+      </el-select>
+    </div>
+    <div v-if="type === 'input'">
+      <el-input v-model="input" :placeholder="placeholder" @input="handleInput"></el-input>
+    </div>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+export default {
+  name: 'search',
+  props: {
+    type: { type: String, default: 'input' },
+    placeholder: { type: String, default: '请输入' },
+    dataList: { type: Array, default: () => [] },
+  },
+  components: {},
+  data: () => ({
+    input: '',
+    newDataList: [],
+  }),
+  created() {},
+  computed: {},
+  mounted() {
+    if (this.type === 'select') {
+      this.$set(this, `newDataList`, this.dataList);
+    }
+  },
+  methods: {
+    handleInput() {
+      this.$emit('input', this.input);
+      return;
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.icons {
+  zoom: 1.5;
+  position: relative;
+  top: 0.5rem;
+}
+</style>

+ 230 - 0
src/components/tag-all-select.vue

@@ -0,0 +1,230 @@
+<template>
+  <div id="tag-all-select">
+    <el-collapse v-model="activeNames" @change="changeDisplay" :style="`text-align:${selectList.length > 0 ? 'left' : 'center'};`">
+      <el-row style="background:#ffffff;">
+        <el-col :span="24" v-if="selectList.length > 0" @click.native="changeDisplay">
+          <el-tag
+            v-for="(item, index) in displayList"
+            :key="index"
+            :closable="true"
+            @close="tagClose(item)"
+            style="margin-right:0.5rem;"
+            :disable-transitions="true"
+          >
+            {{ item.label }}
+          </el-tag>
+        </el-col>
+        <el-col :span="24" style="width:100%" @click.native="changeDisplay" v-else>
+          {{ placeholder }}
+        </el-col>
+      </el-row>
+      <el-collapse-item name="1" style="text-align:left;">
+        <el-row :gutter="10" class="selectCard">
+          <el-col :span="6" class="firstMenu">
+            <el-checkbox-group
+              v-model="selectList"
+              @change="
+                val => {
+                  selectChange(val, 'first');
+                }
+              "
+            >
+              <el-col v-for="(item, index) in firstList" :key="index" class="word">
+                <el-checkbox :label="item.value">{{ item.label }}</el-checkbox>
+              </el-col>
+            </el-checkbox-group>
+          </el-col>
+          <el-col :span="18" style="padding:0 1rem;">
+            <el-checkbox-group
+              v-model="selectList"
+              @change="
+                val => {
+                  selectChange(val, 'second');
+                }
+              "
+            >
+              <el-col :span="6" v-for="(item, index) in secondList" :key="index" class="word">
+                <el-checkbox :label="item.value">{{ item.label }}</el-checkbox>
+              </el-col>
+            </el-checkbox-group>
+          </el-col>
+        </el-row>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+export default {
+  name: 'tag-all-select',
+  props: {
+    selected: { type: Array, default: () => [] }, //已选项
+    placeholder: { type: String, default: '请选择' }, //提示
+    firstList: { type: Array, default: () => [] }, //一级选项列表
+    secondList: { type: Array, default: () => [] }, //二级选项列表
+    type: { type: String },
+  },
+  components: {},
+  data: () => ({
+    activeNames: '',
+    selectList: [],
+    displayList: [],
+  }),
+  created() {},
+  watch: {
+    selected: {
+      handler(val, oval) {
+        let dif = _.difference(val, oval);
+        let key = Object.keys(val[0]);
+        if (dif.length > 0) {
+          this.defaultProcess();
+        }
+      },
+    },
+  },
+  computed: {},
+  methods: {
+    //一/二级选择方法
+    selectChange(val, type) {
+      if (type === 'first') {
+        this.processProvince(val);
+      } else {
+        this.processCity();
+      }
+    },
+    //处理直辖/省份
+    processProvince(val) {
+      //1对比出哪个是新选的,如果没有新选的,那就是取消
+      let is_selected = this.selected.map(item => item.value); //将选择前的结果的value整理出来
+      let different = _.difference(val, is_selected); //对比出新选的value是哪个
+      if (different.length > 0) {
+        //新选的情况
+        //2查看是不是直辖市
+        let haveCityList = [];
+        haveCityList = different.filter(item => {
+          return this.haveCity(item);
+        });
+        //3,不是直辖市,让父组件查询城市列表;并去掉此选项
+        if (haveCityList.length > 0) {
+          //此处操作:将最后一次结果还原,并告诉父组件请求该省下的城市
+          this.$set(this, `selectList`, is_selected);
+          let code = '';
+          if (typeof haveCityList[0] === 'object') {
+            code = haveCityList[0].value;
+          } else {
+            code = haveCityList[0];
+          }
+          this.$emit('listChange', { val: code, type: this.type });
+        } else {
+          //是直辖市/城市,选择此城市,然后过滤出这条数据,用于显示,删除
+          this.processCity();
+        }
+      } else {
+        this.processCity();
+      }
+    },
+    //看着名很咋地,其实就是同步数据
+    processCity() {
+      let newArr = [];
+      this.selectList.map(item => {
+        let result = this.firstList.filter(fil => fil.value === item);
+        if (result.length > 0) {
+          result.forEach(res => {
+            newArr.push(res);
+          });
+        } else {
+          result = this.secondList.filter(fil => fil.value === item);
+          if (result.length > 0) {
+            result.forEach(res => {
+              newArr.push(res);
+            });
+          } else {
+            result = this.displayList.filter(fil => fil.value === item);
+            if (result.length > 0) {
+              result.forEach(res => {
+                newArr.push(res);
+              });
+            }
+          }
+        }
+      });
+      this.disFilter(newArr);
+    },
+    //关闭标签方法
+    tagClose(val) {
+      //取消选择
+      let leastList = this.selectList.filter(item => item !== val.value);
+      let leastDisplayList = this.displayList.filter(item => item.value !== val.value);
+      this.$set(this, `selectList`, leastList);
+      this.disFilter(leastDisplayList);
+    },
+    //是不是直辖市
+    haveCity(item) {
+      let value;
+      if (typeof item === 'object') {
+        value = item.value;
+      } else {
+        value = item;
+      }
+      if (value === '110000') {
+        return false;
+      } else if (value.indexOf('0000') > 0) {
+        return true;
+      } else {
+        return false;
+      }
+    },
+    //显示/隐藏选择面板
+    changeDisplay() {
+      if (this.activeNames !== '') {
+        this.$set(this, `activeNames`, '');
+      } else {
+        this.$set(this, `activeNames`, '1');
+      }
+    },
+    //初始化处理
+    defaultProcess() {
+      //处理复选框
+      let select = this.selected.filter(item => item.value !== undefined);
+      if (select.length > 0) {
+        let result = this.selected.map(item => item.value);
+        this.$set(this, `selectList`, result);
+        //处理显示
+        this.$set(this, `displayList`, this.selected);
+      } else {
+        let result = this.selected.map(item => {
+          return this.defalutSearch(item);
+        });
+        this.disFilter(result);
+      }
+    },
+    //显示处理;选择值
+    disFilter(val) {
+      this.$emit('selectChange', { val: val, type: this.type });
+      this.$set(this, `displayList`, val);
+    },
+    defalutSearch(label) {
+      let result = this.firstList.filter(fil => fil.label === label);
+      if (result.length > 0) {
+        return result[0];
+      } else {
+        result = this.secondList.filter(fil => fil.label === label);
+        if (result.length > 0) {
+          return result[0];
+        }
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.selectCard {
+  border: 1px solid #e7e8ec;
+}
+.firstMenu {
+  background: #c9cacf;
+  border: 1px solid #e7e8ec;
+}
+</style>

+ 137 - 0
src/components/tag-checkbox.vue

@@ -0,0 +1,137 @@
+<template>
+  <div id="tag-checkbox">
+    <el-collapse v-model="activeNames" @change="changeDisplay" :style="`text-align:${selectList.length > 0 ? 'left' : 'center'};`">
+      <el-row style="background:#ffffff;">
+        <el-col :span="24" v-if="selectList.length > 0" @click.native="changeDisplay">
+          <el-tag
+            v-for="(item, index) in displayList"
+            :key="index"
+            :closable="true"
+            @close="tagClose(item)"
+            style="margin-right:0.5rem;"
+            :disable-transitions="true"
+          >
+            {{ item.label }}
+          </el-tag>
+        </el-col>
+        <el-col :span="24" style="width:100%" @click.native="changeDisplay" v-else>
+          {{ placeholder }}
+        </el-col>
+      </el-row>
+      <el-collapse-item name="1" style="text-align:left;">
+        <el-card>
+          <el-row>
+            <el-col :span="24">
+              <el-checkbox-group v-if="max" :max="max" :min="min" v-model="selectList" @change="selectChange">
+                <el-col :span="6" v-for="(item, index) in firstList" :key="index" class="word">
+                  <el-checkbox :label="item.value">{{ item.label }}</el-checkbox>
+                </el-col>
+              </el-checkbox-group>
+              <el-checkbox-group v-else v-model="selectList" @change="selectChange">
+                <el-col :span="6" v-for="(item, index) in firstList" :key="index" class="word">
+                  <el-checkbox :label="item.value">{{ item.label }}</el-checkbox>
+                </el-col>
+              </el-checkbox-group>
+            </el-col>
+          </el-row>
+        </el-card>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+export default {
+  name: 'tag-checkbox',
+  props: {
+    selected: { type: Array, default: () => [] }, //已选项
+    placeholder: { type: String, default: '请选择' }, //提示
+    firstList: { type: Array, default: () => [] }, //一级选项列表
+    max: { type: Number },
+    min: { type: Number },
+    type: { type: String },
+  },
+  components: {},
+  data: () => ({
+    activeNames: '',
+    selectList: [],
+    displayList: [],
+  }),
+  created() {},
+  watch: {
+    selected: {
+      handler(val, oval) {
+        let dif = _.difference(val, oval);
+        if (dif.length > 0) {
+          this.defaultProcess();
+        }
+      },
+    },
+  },
+  computed: {},
+  methods: {
+    selectChange() {
+      let newArr = [];
+      this.selectList.map(item => {
+        let result = this.firstList.filter(fil => fil.value === item);
+        if (result.length > 0) {
+          result.forEach(res => {
+            newArr.push(res);
+          });
+        }
+      });
+      this.disFilter(newArr);
+    },
+    //显示/隐藏选择面板
+    changeDisplay() {
+      if (this.activeNames !== '') {
+        this.$set(this, `activeNames`, '');
+      } else {
+        this.$set(this, `activeNames`, '1');
+      }
+    },
+    //初始化处理
+    defaultProcess() {
+      let select = this.selected.filter(item => item.value !== undefined);
+      if (select.length > 0) {
+        let result = this.selected.map(item => item.value);
+        this.$set(this, `selectList`, result);
+        //处理显示
+        this.$set(this, `displayList`, this.selected);
+      } else {
+        let result = this.selected.map(item => {
+          return this.defalutSearch(item);
+        });
+        this.disFilter(result);
+      }
+    },
+    //显示处理;选择值
+    disFilter(val) {
+      this.$emit('selectChange', { val: val, type: this.type });
+      this.$set(this, `displayList`, val);
+    },
+    //关闭标签方法
+    tagClose(val) {
+      //取消选择
+      let leastList = this.selectList.filter(item => item !== val.value);
+      let leastDisplayList = this.displayList.filter(item => item.value !== val.value);
+      this.$set(this, `selectList`, leastList);
+      this.disFilter(leastDisplayList);
+    },
+    defalutSearch(label) {
+      let result = this.firstList.filter(fil => fil.label === label);
+      if (result.length > 0) {
+        return result[0];
+      } else {
+        result = this.secondList.filter(fil => fil.label === label);
+        if (result.length > 0) {
+          return result[0];
+        }
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 204 - 0
src/components/tag-sec-select.vue

@@ -0,0 +1,204 @@
+<template>
+  <div id="tag-sec-select">
+    <el-collapse v-model="activeNames" @change="changeDisplay" :style="`text-align:${selectList.length > 0 ? 'left' : 'center'};`">
+      <el-row style="background:#ffffff;">
+        <el-col :span="24" v-if="displayList.length > 0" @click.native="changeDisplay">
+          <el-tag
+            v-for="(item, index) in displayList"
+            :key="index"
+            :closable="true"
+            @close="tagClose(item)"
+            style="margin-right:0.5rem;"
+            :disable-transitions="true"
+          >
+            {{ item.label }}
+          </el-tag>
+        </el-col>
+        <el-col :span="24" style="width:100%" @click.native="changeDisplay" v-else>
+          {{ placeholder }}
+        </el-col>
+      </el-row>
+      <el-collapse-item name="1" style="text-align:left;">
+        <el-row :gutter="10" class="selectCard">
+          <el-col :span="6" class="firstMenu">
+            <el-menu background-color="#eeeeee" text-color="#000" active-text-color="#409EFF" @select="selectMenu">
+              <el-menu-item v-for="(item, index) in firstList" :key="index" :index="item.value">
+                <template v-slot="title">
+                  {{ item.label }}
+                </template>
+              </el-menu-item>
+            </el-menu>
+          </el-col>
+          <el-col :span="18" style="padding:1rem 1rem;">
+            <el-row type="flex" style="margin-bottom:1rem;" v-if="secondList.length > 0">
+              <el-switch v-model="allSelect" active-text="全选" inactive-text="" @change="childrenSelect"> </el-switch>
+            </el-row>
+            <el-checkbox-group v-model="selectList" @change="selectChange">
+              <el-col :span="6" v-for="(item, index) in secondList" :key="index" class="word">
+                <el-checkbox :label="item.value">{{ item.label }}</el-checkbox>
+              </el-col>
+            </el-checkbox-group>
+          </el-col>
+        </el-row>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+export default {
+  name: 'tag-sec-select',
+  props: {
+    selected: { type: Array, default: () => [] }, //已选项
+    placeholder: { type: String, default: '请选择' }, //提示
+    firstList: { type: Array, default: () => [] }, //一级选项列表
+    secondList: { type: Array, default: () => [] }, //二级选项列表
+    type: { type: String },
+  },
+  components: {},
+  data: () => ({
+    activeNames: '',
+    selectList: [],
+    displayList: [],
+    allSelect: false,
+  }),
+  mounted() {
+    this.defaultProcess();
+    this.checkIsAll();
+  },
+  created() {
+    if (this.selected.length > 0) {
+      this.defaultProcess();
+    }
+  },
+  computed: {},
+  watch: {
+    selected: {
+      handler(value, oval) {
+        let dif = _.difference(value, oval);
+        if (dif.length > 0) {
+          this.defaultProcess();
+        }
+      },
+    },
+  },
+  methods: {
+    //显示/隐藏选择面板
+    changeDisplay() {
+      if (this.activeNames !== '') {
+        this.$set(this, `activeNames`, '');
+      } else {
+        this.$set(this, `activeNames`, '1');
+      }
+    },
+    //菜单操作
+    async selectMenu(val) {
+      if (val !== '0') {
+        this.cleanUnlimited();
+        await this.$emit('listChange', { val: val, type: this.type });
+      } else {
+        this.$set(this, `displayList`, [{ label: '不限专业', value: '0' }]);
+        this.$set(this, `selectList`, [{ label: '不限专业', value: '0' }]);
+        this.$emit('selectChange', { val: [{ label: '不限专业', value: '0' }], type: this.type });
+      }
+      this.checkIsAll();
+    },
+    //选项操作
+    selectChange() {
+      let newArr = [];
+      this.selectList.map(item => {
+        let result = this.secondList.filter(fil => fil.value === item);
+        if (result.length > 0) {
+          result.forEach(res => {
+            newArr.push(res);
+          });
+        } else {
+          result = this.displayList.filter(fil => fil.value === item);
+          if (result.length > 0) {
+            result.forEach(res => {
+              newArr.push(res);
+            });
+          }
+        }
+      });
+      this.disFilter(newArr);
+      this.checkIsAll();
+    },
+    //不限专业
+    cleanUnlimited() {
+      let result = this.displayList.filter(item => item.value !== '0');
+      this.$set(this, `displayList`, result);
+      this.$set(this, `allSelect`, false);
+    },
+    //全/反选
+    childrenSelect(val) {
+      //本子列表全选及反选
+      let newArr = [];
+      let result = [];
+      this.secondList.map(item => {
+        newArr.push(item.value);
+      });
+      if (val) {
+        result = _.uniq(_.concat(this.selectList, newArr));
+      } else {
+        result = _.differenceWith(this.selectList, newArr);
+      }
+      this.$set(this, `selectList`, result);
+      this.selectChange();
+    },
+    //该页选择全部选项,自动切换全选;该页未全部选择,去掉全选
+    checkIsAll() {
+      if (this.secondList.length > 0) {
+        let result = true;
+        for (const item of this.secondList) {
+          let i = 0;
+          for (i = 0; i < this.selectList.length; i++) {
+            const select = this.selectList[i];
+            if (item.value === select) {
+              break;
+            }
+          }
+          if (i === this.selectList.length) {
+            result = false;
+            break;
+          }
+        }
+        this.$set(this, `allSelect`, result);
+      }
+    },
+    //初始化处理
+    defaultProcess() {
+      //处理复选框
+      let select = this.selected.map(item => item.value);
+      this.$set(this, `selectList`, select);
+      //处理显示
+      this.$set(this, `displayList`, this.selected);
+    },
+    //显示处理;选择值
+    disFilter(val) {
+      this.$emit('selectChange', { val: val, type: this.type });
+      this.$set(this, `displayList`, val);
+    },
+    //关闭标签方法
+    tagClose(val) {
+      //取消选择
+      let leastList = this.selectList.filter(item => item !== val.value);
+      let leastDisplayList = this.displayList.filter(item => item.value !== val.value);
+      this.$set(this, `selectList`, leastList);
+      this.disFilter(leastDisplayList);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.selectCard {
+  border: 1px solid #e7e8ec;
+}
+.el-menu-item {
+  height: 2rem;
+  line-height: 2rem;
+  text-align: center;
+}
+</style>

+ 152 - 0
src/components/upload-file.vue

@@ -0,0 +1,152 @@
+<template>
+  <div id="upload-file">
+    <el-upload
+      ref="uploadFile"
+      :action="url"
+      :before-remove="handleRemove"
+      :before-upload="changeFile"
+      :on-success="onSuccess"
+      :limit="limit"
+      multiple
+      :on-exceed="outLimit"
+      :file-list="fileList"
+      :on-preview="onPreview"
+      :on-remove="remove"
+    >
+      <el-button slot="trigger" size="small" type="primary">选取文件</el-button>
+      <div slot="tip" class="el-upload__tip">{{ desc === undefined ? '只能上传不超过1MB的文件' : desc }}</div>
+    </el-upload>
+    <el-dialog title="查看" :visible.sync="dialog" center width="90%">
+      <el-row>
+        <el-form :model="disObject" label-position="left" label-width="auto">
+          <el-form-item label="文件名" prop="name">
+            <el-input v-model="disObject.name" placeholder="请输入新文件名"></el-input>
+          </el-form-item>
+          <el-image style="width: 100%; height: 100%" :src="disObject.url" fit="scale-down" v-if="disObject.type === 'pic'"></el-image>
+        </el-form>
+      </el-row>
+      <template #footer>
+        <el-row type="flex" align="middle" justify="center">
+          <el-col :span="8"><el-button type="info" size="small" @click="dialog = false">返回 </el-button></el-col>
+          <el-col :span="8" v-if="disObject.type === 'file'"><el-button size="small" @click="downLoad(disObject.url)">下载</el-button></el-col>
+          <el-col :span="8"><el-button type="primary" size="small" @click="changeName()">修改文件名</el-button></el-col>
+        </el-row>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+export default {
+  name: 'upload-file',
+  props: {
+    url: { type: null },
+    type: { type: String },
+    limit: { type: Number },
+    data: { type: null },
+    desc: { type: String },
+  },
+  components: {},
+  data: () => ({
+    fileList: [],
+    dialog: false,
+    disObject: {},
+    test: {},
+  }),
+  created() {
+    if (this.data) {
+      this.defalutProcess(this.data);
+    }
+  },
+  computed: {},
+  watch: {
+    data: {
+      handler(val) {
+        this.defalutProcess(val);
+      },
+    },
+  },
+  methods: {
+    handleRemove(file) {
+      return true;
+    },
+    changeFile(file, fileList) {
+      let size = file.size / 1024 / 1024;
+      if (size > 2) {
+        return false;
+      }
+    },
+    onSuccess(response, file, fileList) {
+      //将文件整理好传回父组件
+      this.$emit('upload', { type: this.type, data: response });
+    },
+    outLimit() {
+      this.$message.error(`只允许上传${this.limit}个文件`);
+    },
+    onPreview(file) {
+      let duplicate = JSON.parse(JSON.stringify(file));
+      let url = duplicate.url !== undefined ? duplicate.url : duplicate.response.uri;
+      let res = this.isPic(url);
+      if (res) {
+        this.disObject.type = 'pic';
+      } else {
+        this.disObject.type = 'file';
+      }
+      this.disObject.url = url;
+      this.dialog = true;
+    },
+    defalutProcess(val) {
+      if (typeof val === 'object' && _.get(val, length) !== undefined && val.length > 0) {
+        let newArr = [];
+        val.map(item => {
+          let object = {};
+          object.name = item.name;
+          object.url = `${item.uri}`;
+          newArr.push(object);
+        });
+        this.$set(this, `fileList`, newArr);
+      } else if (typeof val === 'object' && _.get(val, length) === undefined) {
+        let object = {};
+        object.name = val.name;
+        object.url = `${val.uri}`;
+        this.$set(this, `fileList`, [object]);
+      } else if (typeof val === 'string') {
+        this.$set(this, `fileList`, [{ name: '附件', url: val }]);
+      }
+    },
+    isPic(url) {
+      if (url.includes('.jpg')) {
+        return true;
+      }
+      if (url.includes('.bmp')) {
+        return true;
+      }
+      if (url.includes('.jpge')) {
+        return true;
+      }
+      if (url.includes('.png')) {
+        return true;
+      }
+      if (url.includes('.gif')) {
+        return true;
+      }
+    },
+    downLoad(url) {
+      window.open(url);
+    },
+    changeName() {
+      this.$emit('changeName', { type: this.type, data: this.disObject });
+      this.dialog = false;
+      this.disObject = {};
+    },
+    remove(file) {
+      let newFileList = this.fileList.filter(file => file.url !== file.url);
+      this.$set(this, `fileList`, newFileList);
+      this.$emit('toRemove', { type: this.type, data: file });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 75 - 0
src/components/upload.vue

@@ -0,0 +1,75 @@
+<template>
+  <div id="upload">
+    <el-upload
+      v-if="url"
+      ref="upload"
+      :action="url"
+      list-type="picture-card"
+      :file-list="fileList"
+      :limit="limit"
+      :on-exceed="outLimit"
+      :on-preview="handlePictureCardPreview"
+      :before-remove="handleRemove"
+      :on-success="onSuccess"
+    >
+      <template>
+        <i class="el-icon-plus"></i>
+      </template>
+    </el-upload>
+    <el-dialog :visible.sync="dialogVisible">
+      <img width="100%" :src="dialogImageUrl" alt="" />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'upload',
+  props: {
+    url: { type: null },
+    limit: { type: Number },
+    data: { type: null },
+    type: { type: String },
+  },
+  components: {},
+  data: () => ({
+    dialogVisible: false,
+    dialogImageUrl: '',
+    fileList: [],
+  }),
+  created() {
+    if (this.data) {
+      this.defalutProcess(this.data);
+    }
+  },
+  watch: {
+    data: {
+      handler(val) {
+        this.defalutProcess(val);
+      },
+    },
+  },
+  computed: {},
+  methods: {
+    handlePictureCardPreview(file) {
+      this.dialogImageUrl = file.url;
+      this.dialogVisible = true;
+    },
+    handleRemove(file) {
+      return true;
+    },
+    outLimit() {
+      this.$message.error('只允许上传1张头像');
+    },
+    onSuccess(response, file, fileList) {
+      //将文件整理好传回父组件
+      this.$emit('upload', { type: this.type, data: response });
+    },
+    defalutProcess(val) {
+      this.$set(this, `fileList`, [{ name: this.type, url: this.data }]);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 77 - 0
src/components/wang-editor.vue

@@ -0,0 +1,77 @@
+<template>
+  <div ref="editor" style="text-align:left"></div>
+</template>
+<script>
+import E from 'wangeditor';
+
+const menus = [
+  'head', // 标题
+  'bold', // 粗体
+  'fontSize', // 字号
+  'fontName', // 字体
+  'italic', // 斜体
+  'underline', // 下划线
+  'strikeThrough', // 删除线
+  'foreColor', // 文字颜色
+  'backColor', // 背景颜色
+  'link', // 插入链接
+  'list', // 列表
+  'justify', // 对齐方式
+  'quote', // 引用
+  // 'emoticon', // 表情
+  'table', // 表格
+  // 'video', // 插入视频
+  // 'code', // 插入代码
+  'undo', // 撤销
+  'redo', // 重复
+];
+
+export default {
+  name: 'wang-editor',
+  model: {
+    prop: 'value',
+    event: 'change', // 默认为input时间,此处改为change
+  },
+  props: {
+    value: { type: String, required: false, default: '' },
+  },
+  data() {
+    return {
+      editorContent: this.value,
+    };
+  },
+  mounted() {
+    var editor = new E(this.$refs.editor);
+    editor.customConfig.onchange = html => {
+      this.editorContent = html;
+      this.$emit('change', html);
+    };
+    // 自定义菜单配置
+    editor.customConfig.menus = menus;
+    editor.customConfig.zIndex = 0;
+    editor.customConfig.uploadImgServer = '/files/cms/images/upload';
+    editor.customConfig.uploadImgMaxLength = 1;
+    editor.customConfig.uploadImgHooks = {
+      // 如果服务器端返回的不是 {errno:0, data: [...]} 这种格式,可使用该配置
+      // (但是,服务器端返回的必须是一个 JSON 格式字符串!!!否则会报错)
+      customInsert: function(insertImg, result, editor) {
+        // 图片上传并返回结果,自定义插入图片的事件(而不是编辑器自动插入图片!!!)
+        // insertImg 是插入图片的函数,editor 是编辑器对象,result 是服务器端返回的结果
+
+        // 举例:假如上传图片成功后,服务器端返回的是 {url:'....'} 这种格式,即可这样插入图片:
+        var url = result.uri;
+        insertImg(url);
+
+        // result 必须是一个 JSON 格式字符串!!!否则报错
+      },
+    };
+    editor.create();
+    editor.txt.html(this.value);
+  },
+  methods: {
+    getContent: function() {
+      return this.editorContent;
+    },
+  },
+};
+</script>

+ 37 - 0
src/layout/detail-layout.vue

@@ -0,0 +1,37 @@
+<template>
+  <div id="detail-layout">
+    <el-row class="title_space">
+      <el-col :span="24">
+        <slot name="title"> </slot>
+        <!-- :xxx=>object name;此处相当于把我们要传过去的参数封了一层: {data:test} -->
+      </el-col>
+    </el-row>
+    <div class="bord">
+      <slot name="main"></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'detail-layout',
+  props: {},
+  components: {},
+  data: () => ({}),
+  created() {},
+  computed: {},
+  methods: {},
+};
+</script>
+
+<style lang="less" scoped>
+.title_space {
+  margin: 0.9rem 0;
+  font-size: 1rem;
+}
+.bord {
+  margin: 1rem 0;
+  background: #f4f5f9;
+  // border: 1px solid #e7e8ec;
+}
+</style>

+ 226 - 0
src/layout/layout-part/heads.vue

@@ -0,0 +1,226 @@
+<template>
+  <div id="heads">
+    <el-row>
+      <div class="headTop">
+        <a href="" class="headImg">
+          <img src="../../assets/default_logo.png" width="60" height="60" />
+        </a>
+        <!-- <a class="headXinyong">信用等级</a> -->
+        <h3 class="headCompany">{{ user.corpname }}</h3>
+      </div>
+      <div class="headBtn">
+        <el-popover placement="left-start" title="" width="400" trigger="hover">
+          <div class="popover-content" v-html="html"></div>
+          <el-button slot="reference"><span class="el-icon-message info_icon"></span></el-button>
+        </el-popover>
+        <!-- <span class="mr14">|</span> -->
+        <!-- <a href="" class="mr14" style="color:red" title="云校招企业服务平台简明使用手册"> <span class="el-icon-question info_icon"></span> 帮助 </a> -->
+        <span class="mr14">|</span>
+        <span class="mr14">
+          <span class="el-icon-s-custom info_icon"></span>
+          &nbsp;{{ user.name }}
+        </span>
+        <a class="mr14" @click="dialog = true">
+          <el-link class="el-icon-edit-outline info_icon">修改密码</el-link>
+        </a>
+        <span class="mr14">|</span>
+        <a href="" style="color: #333;"
+          ><span class="el-icon-switch-button info_icon"></span>
+          &nbsp;退出
+        </a>
+      </div>
+    </el-row>
+    <el-dialog width="30%" title="修改密码" :visible.sync="dialog">
+      <el-form :model="form" :rules="rules" ref="form" label-width="auto" label-position="left">
+        <el-form-item label="新密码:" prop="passwd">
+          <el-input v-model="form.passwd" autocomplete="off" type="password" show-password></el-input>
+        </el-form-item>
+        <!-- <el-form-item label="重复输入新密码:">
+          <el-input v-model="form.date2" autocomplete="off"></el-input>
+        </el-form-item> -->
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="toClose">取 消</el-button>
+        <el-button type="success" @click="toSubmit">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { mapActions, mapState, mapMutations } from 'vuex';
+const jwt = require('jsonwebtoken');
+export default {
+  name: 'heads',
+  props: {},
+  components: {},
+  data: () => ({
+    dialog: false,
+    form: {},
+    rules: {
+      passwd: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
+    },
+    formLabelWidth: '120px',
+    html: '<ul class="popoverCon"><li>没有消息</li></ul><a class="popoverBtn">点击进入信息中心<span class="el-icon-d-arrow-right info_icon"></span></a>',
+  }),
+  created() {
+    this.checkUrl();
+    this.setUser();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapMutations(['setUser']),
+    ...mapActions(['userOperation']),
+    checkUrl() {
+      if (this.user.corpid) return;
+      if (location.search.length > 0) {
+        let params = location.search.replace('?', '').split('=');
+        let key = params[0];
+        if (key.includes(`token`)) {
+          let token = params[1];
+          let info = jwt.decode(token);
+          this.setUser(info);
+          let turnTo = window.location.href.replace(window.location.search, '');
+          window.location.href = turnTo;
+        }
+      }
+    },
+    toClose() {
+      this.$refs.form.resetFields();
+      this.dialog = false;
+    },
+    toSubmit() {
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          this.submit();
+        }
+        return false;
+      });
+    },
+    async submit() {
+      let result = await this.userOperation({ type: 'passwd', data: { info: this.form, id: this.user.id } });
+      this.$message({
+        type: `${result.errcode}` === '0' ? 'success' : 'error',
+        message: `${result.errcode}` === '0' ? '修改成功' : result.errmsg,
+      });
+      if (`${result.errcode}` === '0') {
+        this.form = {};
+        this.dialog = false;
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+img {
+  max-height: 100%;
+  max-width: 100%;
+}
+a {
+  text-decoration: none;
+}
+.headTop {
+  float: left;
+  width: 50%;
+  padding: 0 0 0 50px;
+}
+.headImg {
+  float: left;
+  width: 65px;
+  height: 60px;
+}
+.headXinyong {
+  float: left;
+  width: 70px;
+  height: 40px;
+  margin: 13px 0 0 10px;
+}
+.headCompany {
+  float: left;
+  font-size: 21px;
+  width: 244px;
+  padding: 18px 0;
+  margin: 0;
+  color: #333;
+  font-family: 'inherit';
+  font-weight: 500;
+}
+.headBtn {
+  float: right;
+  width: 40%;
+  text-align: right;
+  padding: 0 60px 0 0;
+  margin-top: 20px;
+  font-size: 14px;
+  color: #333;
+}
+.headBtn .mr14 {
+  margin-right: 14px;
+  color: #333;
+}
+.headBtn .el-button {
+  border-radius: 0px;
+  border: none;
+  width: 0;
+  height: 0px;
+}
+.headBtn .el-button:hover {
+  background: none;
+}
+.popover-content {
+  margin: 0;
+  padding: 0;
+}
+/deep/.el-popper {
+  border: 1px solid #ccc;
+  border-radius: 0px;
+  height: 100px;
+  padding: 0;
+}
+/deep/.popoverCon {
+  width: 400px;
+  height: 70px;
+  text-align: center;
+  padding: 0;
+  margin: 0;
+}
+/deep/.popoverCon li {
+  list-style: none;
+}
+/deep/.popoverBtn {
+  position: absolute;
+  bottom: 0;
+  left: 0px;
+  width: 424px;
+  height: 30px;
+  line-height: 30px;
+  text-align: right;
+  color: #333;
+  border-radius: 0 0 4px 4px;
+  background: #f6f6f6;
+  margin: 0;
+  padding: 0;
+}
+/deep/.popoverBtn:hover {
+  text-decoration: underline;
+  color: #409eff;
+}
+/deep/.el-form-item__label {
+  padding: 0px 0px 0 0;
+}
+/deep/.el-dialog__header {
+  background-color: #40a44c;
+}
+/deep/.el-icon-close:before {
+  color: #000;
+}
+/deep/.el-dialog__footer {
+  padding: 0px 20px 10px;
+}
+/deep/.el-dialog__body {
+  padding: 15px 20px;
+}
+</style>

+ 81 - 0
src/layout/layout-part/menus.vue

@@ -0,0 +1,81 @@
+<template>
+  <div id="menus">
+    <el-menu :default-active="thisRouter()" class="el-menu-vertical-demo" :router="false" :default-openeds="defalutMenu" @select="to">
+      <!-- <el-submenu index="4">
+        <template v-slot:title>
+          <i class="el-icon-office-building"></i>
+          <span>测试</span>
+        </template>
+        <el-menu-item-group>
+          <el-menu-item index="/list-normal">列表1</el-menu-item>
+          <el-menu-item index="/list-tab">列表2</el-menu-item>
+          <el-menu-item index="/test-detail">详情</el-menu-item>
+        </el-menu-item-group>
+      </el-submenu> -->
+      <el-menu-item index="/"> <i class="el-icon-s-grid"></i>首页 </el-menu-item>
+      <el-submenu index="1">
+        <template v-slot:title>
+          <i class="el-icon-files"></i>
+          <span>校园招聘</span>
+        </template>
+        <el-menu-item-group>
+          <el-menu-item index="/jobs/school/index">入驻学校</el-menu-item>
+          <el-menu-item index="/jobs/profile/index">招聘简章</el-menu-item>
+          <el-menu-item index="/manager/jobs/index">职位管理</el-menu-item>
+          <el-menu-item index="/jobs/talk/index">宣讲会</el-menu-item>
+          <el-menu-item index="/jobs/jobfair/index">招聘会</el-menu-item>
+          <el-menu-item index="/jobs/jobinfo/index">在线招聘</el-menu-item>
+        </el-menu-item-group>
+      </el-submenu>
+      <el-submenu index="2">
+        <template v-slot:title>
+          <i class="el-icon-document-copy"></i>
+          <span>招聘管理</span>
+        </template>
+        <el-menu-item-group>
+          <el-menu-item index="/manager/resume/index">简历管理</el-menu-item>
+          <!-- <el-menu-item index="/manager/invite/index">人才邀约</el-menu-item>
+          <el-menu-item index="/manager/record/index">邀约记录</el-menu-item> -->
+          <!-- <el-menu-item index="/manager/deliver/index">最近投递</el-menu-item> -->
+          <!-- <el-menu-item index="/manager/plan/index">面试安排</el-menu-item> -->
+        </el-menu-item-group>
+      </el-submenu>
+      <el-submenu index="3">
+        <template v-slot:title>
+          <i class="el-icon-office-building"></i>
+          <span>公司信息</span>
+        </template>
+        <el-menu-item-group>
+          <el-menu-item index="/info/base/index">基本资料</el-menu-item>
+          <el-menu-item index="/info/hr/index">HR成员</el-menu-item>
+        </el-menu-item-group>
+      </el-submenu>
+    </el-menu>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'menus',
+  props: {},
+  components: {},
+  data: () => ({
+    defalutMenu: ['1', '2', '3'],
+    defalutPage: '',
+  }),
+  created() {},
+  computed: {},
+  methods: {
+    thisRouter() {
+      return this.$route.path;
+    },
+    to(index) {
+      if (this.$isBindWx()) {
+        this.$router.push({ path: index });
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 57 - 0
src/layout/list-normal.vue

@@ -0,0 +1,57 @@
+<template>
+  <div id="listNormal">
+    <el-row class="title_space">
+      <el-col :span="24">
+        <slot name="title"> </slot>
+        <!-- :xxx=>object name;此处相当于把我们要传过去的参数封了一层: {data:test} -->
+      </el-col>
+    </el-row>
+    <div class="distance30 top_down10">
+      <el-row :gutter="10">
+        <el-col :span="21">
+          <slot name="functionBar"></slot>
+        </el-col>
+        <el-col :span="3">
+          <slot name="addBtn"> </slot>
+        </el-col>
+      </el-row>
+    </div>
+    <div>
+      <slot name="batch"></slot>
+    </div>
+    <div class="distance10 down30">
+      <slot name="main"></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'listNormal',
+  props: {},
+  components: {},
+  data: () => ({}),
+  created() {},
+  computed: {},
+  methods: {},
+};
+</script>
+
+<style lang="less" scoped>
+.distance30 {
+  margin-top: 1.9rem;
+}
+.title_space {
+  margin: 0.9rem 0;
+  font-size: 1rem;
+}
+.distance10 {
+  margin-top: 0.62rem;
+}
+.down30 {
+  margin-bottom: 1.9rem;
+}
+.top_down10 {
+  margin: 1rem 0;
+}
+</style>

+ 57 - 0
src/layout/list-tab.vue

@@ -0,0 +1,57 @@
+<template>
+  <div id="list-tab">
+    <el-row class="title_space">
+      <el-col :span="24">
+        <slot name="title"> </slot>
+      </el-col>
+    </el-row>
+    <el-tabs v-model="activeName" type="card" @tab-click="handleClick">
+      <div class="distance30 top_down10">
+        <el-row :gutter="10">
+          <el-col :span="20">
+            <slot name="functionBar"></slot>
+          </el-col>
+          <el-col :span="3">
+            <slot name="addBtn"> </slot>
+          </el-col>
+        </el-row>
+      </div>
+      <el-tab-pane v-for="(item, index) in tab" :key="index" :label="item" :name="`${index}`">
+        <slot :name="`main${index + 1}`"></slot>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'list-tab',
+  props: {
+    tab: { type: Array, default: () => [] },
+  },
+  components: {},
+  data: () => ({
+    activeName: '0',
+  }),
+  created() {},
+  computed: {},
+  methods: {
+    handleClick(tab) {
+      this.$emit('changeTab', this.activeName);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.distance30 {
+  margin-top: 1.9rem;
+}
+.title_space {
+  margin: 0.9rem 0;
+  font-size: 1rem;
+}
+.top_down10 {
+  margin: 0.62rem 0;
+}
+</style>

+ 74 - 0
src/layout/main-layout.vue

@@ -0,0 +1,74 @@
+<template>
+  <div id="main-layout" @click="checkBind()">
+    <el-container style="background:#e7e8eb;">
+      <el-header height="4rem" class="head">
+        <heads></heads>
+      </el-header>
+      <el-container class="contain">
+        <el-aside width="13rem" class="side">
+          <menus></menus>
+        </el-aside>
+        <el-main class="main">
+          <router-view />
+        </el-main>
+      </el-container>
+    </el-container>
+    <bind :display="dialog" v-if="!$isBindWx()">
+      <el-col :span="24" style="text-align:center;font-size:1.25rem;padding-top:1rem;">
+        绑定微信,开始您的招聘!
+      </el-col>
+    </bind>
+  </div>
+</template>
+
+<script>
+import bind from '@/components/bind.vue';
+import heads from '@/layout/layout-part/heads.vue';
+import menus from '@/layout/layout-part/menus.vue';
+export default {
+  name: 'main-layout',
+  props: {},
+  components: {
+    menus,
+    heads,
+    bind,
+  },
+  data: () => ({
+    dialog: false,
+  }),
+  created() {},
+  computed: {},
+  methods: {
+    checkBind() {
+      if (!this.$isBindWx()) this.dialog = true;
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.head {
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  background: #fff;
+}
+.contain {
+  height: 100%;
+  margin: 2rem 3.5rem;
+  border: 1px solid #a2a2b6;
+  background: #eeeeee;
+}
+.side {
+  margin: 0;
+  padding: 0;
+  width: 2rem;
+  border-right: 1px solid #a2a2b6;
+  background: #ffffff;
+}
+.main {
+  margin: 0;
+  padding: 0 2rem;
+  background: #ffffff;
+}
+</style>

+ 23 - 0
src/main.js

@@ -0,0 +1,23 @@
+import Vue from 'vue';
+import App from './App.vue';
+import router from './router';
+import store from './store';
+import '@/plugins/element.js';
+import '@/plugins/axios';
+import '@/plugins/check-res';
+import '@/plugins/meta';
+import '@/plugins/filters';
+import '@/plugins/loading';
+import '@/style/element.less';
+import '@/plugins/var';
+import '@/plugins/methods';
+import '@/plugins/setting';
+import InitStomp from '@/plugins/stomp';
+Vue.config.productionTip = false;
+
+new Vue({
+  router,
+  store,
+  render: h => h(App),
+}).$mount('#app');
+InitStomp();

+ 19 - 0
src/plugins/axios.js

@@ -0,0 +1,19 @@
+import Vue from 'vue';
+import AxiosWrapper from '@/util/axios-wrapper';
+
+const Plugin = {
+  install(vue, options) {
+    // 3. 注入组件
+    vue.mixin({
+      created() {
+        if (this.$store && !this.$store.$axios) {
+          this.$store.$axios = this.$axios;
+        }
+      },
+    });
+    // 4. 添加实例方法
+    vue.prototype.$axios = new AxiosWrapper(options);
+  },
+};
+
+Vue.use(Plugin, { baseUrl: process.env.VUE_APP_AXIOS_BASE_URL });

+ 39 - 0
src/plugins/check-res.js

@@ -0,0 +1,39 @@
+/* eslint-disable no-underscore-dangle */
+/* eslint-disable no-param-reassign */
+/* eslint-disable no-unused-vars */
+/* eslint-disable no-shadow */
+import Vue from 'vue';
+import _ from 'lodash';
+import { Message } from 'element-ui';
+
+const vm = new Vue({});
+const Plugin = {
+  install(Vue, options) {
+    // 4. 添加实例方法
+    Vue.prototype.$checkRes = (res, okText, errText) => {
+      let _okText = okText;
+      let _errText = errText;
+      if (!_.isFunction(okText) && _.isObject(okText) && okText != null) {
+        ({ okText: _okText, errText: _errText } = okText);
+      }
+      const { errcode = 0, errmsg } = res || {};
+      if (errcode === 0) {
+        if (_.isFunction(_okText)) {
+          return _okText();
+        }
+        if (_okText) {
+          Message.success(_okText);
+        }
+        return true;
+      }
+      if (_.isFunction(_errText)) {
+        return _errText();
+      }
+      Message.error(_errText || errmsg);
+      // Message({ message: _errText || errmsg, duration: 60000 });
+      return false;
+    };
+  },
+};
+
+Vue.use(Plugin);

+ 5 - 0
src/plugins/element.js

@@ -0,0 +1,5 @@
+import Vue from 'vue';
+import Element from 'element-ui';
+import 'element-ui/lib/theme-chalk/index.css';
+
+Vue.use(Element);

+ 6 - 0
src/plugins/filters.js

@@ -0,0 +1,6 @@
+import Vue from 'vue';
+import filters from '@/util/filters';
+
+for (const method in filters) {
+  Vue.filter(method, filters[method]);
+}

+ 27 - 0
src/plugins/loading.js

@@ -0,0 +1,27 @@
+/* eslint-disable no-console */
+/* eslint-disable no-param-reassign */
+
+import Vue from 'vue';
+
+const Plugin = {
+  // eslint-disable-next-line no-unused-vars
+  install(vue, options) {
+    // 3. 注入组件
+    vue.mixin({
+      created() {
+        // eslint-disable-next-line no-underscore-dangle
+        const isRoot = this.constructor === Vue;
+        // console.log(`rootId:${rootVue_uid}; thisId:${this._uid}`);
+        // if (rootVue_uid !== 3) {
+        //   console.log(this);
+        // }
+        if (isRoot) {
+          const el = document.getElementById('loading');
+          if (el) el.style.display = 'none';
+        }
+      },
+    });
+  },
+};
+
+Vue.use(Plugin, { baseUrl: process.env.VUE_APP_AXIOS_BASE_URL });

+ 4 - 0
src/plugins/meta.js

@@ -0,0 +1,4 @@
+import Vue from 'vue';
+import Meta from 'vue-meta';
+
+Vue.use(Meta);

+ 33 - 0
src/plugins/methods.js

@@ -0,0 +1,33 @@
+import Vue from 'vue';
+import _ from 'lodash';
+const Plugin = {
+  install(Vue, options) {
+    // 3. 注入组件
+    Vue.mixin({
+      created() {
+        if (this.$store && !this.$store.$toUndefined) {
+          this.$store.$toUndefined = this.$toUndefined;
+        }
+      },
+    });
+    // 4. 添加实例方法
+    Vue.prototype.$toUndefined = object => {
+      let keys = Object.keys(object);
+      keys.map(item => {
+        object[item] = object[item] === '' ? (object[item] = undefined) : object[item];
+      });
+      return object;
+    };
+    Vue.prototype.$isBindWx = () => {
+      let user = sessionStorage.getItem('user');
+      if (user) {
+        user = JSON.parse(user);
+        return _.get(user, `openid`, false);
+      } else {
+        return false;
+      }
+    };
+  },
+};
+
+Vue.use(Plugin);

+ 20 - 0
src/plugins/setting.js

@@ -0,0 +1,20 @@
+import Vue from 'vue';
+
+Vue.config.weixin = {
+  // baseUrl: process.env.BASE_URL + 'weixin',
+  baseUrl: 'http://smart.cc-lotus.info/weixin',
+};
+
+Vue.config.stomp = {
+  // brokerURL: 'ws://192.168.1.190:15674/ws',
+  brokerURL: '/ws', // ws://${location.host}/ws
+  connectHeaders: {
+    host: 'smart',
+    login: 'web',
+    passcode: 'web123',
+  },
+  // debug: true,
+  reconnectDelay: 5000,
+  heartbeatIncoming: 4000,
+  heartbeatOutgoing: 4000,
+};

+ 65 - 0
src/plugins/stomp.js

@@ -0,0 +1,65 @@
+/**
+ * 基于WebStomp的消息处理插件
+ */
+
+import Vue from 'vue';
+import _ from 'lodash';
+import assert from 'assert';
+import { Client } from '@stomp/stompjs/esm5/client';
+
+const Plugin = {
+  install(Vue, options) {
+    assert(_.isObject(options));
+    if (options.debug && !_.isFunction(options.debug)) {
+      options.debug = str => {
+        console.log(str);
+      };
+    }
+    assert(_.isString(options.brokerURL));
+    if (!options.brokerURL.startsWith('ws://')) {
+      options.brokerURL = `ws://${location.host}${options.brokerURL}`;
+    }
+
+    // 3. 注入组件
+    Vue.mixin({
+      beforeDestroy: function() {
+        if (this.$stompClient) {
+          this.$stompClient.deactivate();
+          delete this.$stompClient;
+        }
+      },
+    });
+
+    // 4. 添加实例方法
+    Vue.prototype.$stomp = function(subscribes = {}) {
+      // connect to mq
+      const client = new Client(options);
+      client.onConnect = frame => {
+        // Do something, all subscribes must be done is this callback
+        // This is needed because this will be executed after a (re)connect
+        console.log('[stomp] connected');
+        Object.keys(subscribes)
+          .filter(p => _.isFunction(subscribes[p]))
+          .forEach(key => {
+            client.subscribe(key, subscribes[key]);
+          });
+      };
+
+      client.onStompError = frame => {
+        // Will be invoked in case of error encountered at Broker
+        // Bad login/passcode typically will cause an error
+        // Complaint brokers will set `message` header with a brief message. Body may contain details.
+        // Compliant brokers will terminate the connection after any error
+        console.log('Broker reported error: ' + frame.headers['message']);
+        console.log('Additional details: ' + frame.body);
+      };
+
+      client.activate();
+
+      this.$stompClient = client;
+    };
+  },
+};
+export default () => {
+  Vue.use(Plugin, Vue.config.stomp);
+};

+ 10 - 0
src/plugins/var.js

@@ -0,0 +1,10 @@
+import Vue from 'vue';
+
+const Plugin = {
+  install(vue, options) {
+    // 4. 添加实例方法
+    vue.prototype.$limit = 15;
+  },
+};
+
+Vue.use(Plugin);

+ 123 - 0
src/router.js

@@ -0,0 +1,123 @@
+import Vue from 'vue';
+import Router from 'vue-router';
+Vue.use(Router);
+
+export default new Router({
+  mode: 'history',
+  base: process.env.VUE_APP_ROUTER,
+  routes: [
+    {
+      path: '/',
+      name: 'home',
+      component: () => import('./views/home/index.vue'),
+    },
+    {
+      path: '/info/base/index',
+      component: () => import('./views/info/base/index.vue'),
+    },
+    {
+      path: '/list-normal',
+      component: () => import('./views/test/test-list-normal.vue'),
+    },
+    {
+      path: '/list-tab',
+      component: () => import('./views/test/test-list-tab.vue'),
+    },
+    {
+      path: '/test-detail',
+      component: () => import('./views/test/test-detail.vue'),
+    },
+    {
+      path: '/manager/invite/index',
+      component: () => import('./views/manager/invite/index.vue'),
+    },
+    {
+      path: '/manager/jobs/index',
+      component: () => import('./views/manager/jobs/index.vue'),
+    },
+    {
+      path: '/manager/jobs/detail',
+      component: () => import('./views/manager/jobs/detail.vue'),
+    },
+    {
+      path: '/manager/jobs/resume',
+      component: () => import('./views/manager/jobs/resume.vue'),
+    },
+    {
+      path: '/manager/plan/index',
+      component: () => import('./views/manager/plan/index.vue'),
+    },
+    {
+      path: '/manager/plan/detail',
+      component: () => import('./views/manager/plan/detail.vue'),
+    },
+    {
+      path: '/jobs/school/index',
+      component: () => import('./views/jobs/school/index.vue'),
+    },
+    {
+      path: '/jobs/school/detail',
+      component: () => import('./views/jobs/school/detail.vue'),
+    },
+    {
+      path: '/jobs/profile/index',
+      component: () => import('./views/jobs/profile/index.vue'),
+    },
+    {
+      path: '/jobs/profile/detail',
+      component: () => import('./views/jobs/profile/detail.vue'),
+    },
+    {
+      path: '/manager/record/index',
+      component: () => import('./views/manager/record/index.vue'),
+    },
+    {
+      path: '/jobs/jobinfo/index',
+      component: () => import('./views/jobs/jobinfo/index.vue'),
+    },
+    {
+      path: '/jobs/jobinfo/detail',
+      component: () => import('./views/jobs/jobinfo/detail.vue'),
+    },
+    {
+      path: '/manager/resume/index',
+      component: () => import('./views/manager/resume/index.vue'),
+    },
+    {
+      path: '/manager/deliver/index',
+      component: () => import('./views/manager/deliver/index.vue'),
+    },
+    {
+      path: '/info/hr/index',
+      component: () => import('./views/info/hr/index.vue'),
+    },
+    {
+      path: '/jobs/talk/index',
+      component: () => import('./views/jobs/talk/index.vue'),
+    },
+    {
+      path: '/jobs/talk/detail',
+      component: () => import('./views/jobs/talk/detail.vue'),
+    },
+    {
+      path: '/jobs/jobfair/index',
+      component: () => import('./views/jobs/jobfair/index.vue'),
+    },
+    {
+      path: '/jobs/jobfair/detail',
+      component: () => import('./views/jobs/jobfair/detail.vue'),
+    },
+    {
+      path: '/jobs/jobfair/info',
+      component: () => import('./views/jobs/jobfair/info.vue'),
+    },
+    {
+      path: '/manager/plan/notify/record',
+      component: () => import('./views/manager/plan/notify-record.vue'),
+    },
+    {
+      path: '/manager/plan/interview/detail',
+      component: () => import('./views/manager/plan/interview-detail.vue'),
+    },
+  ],
+});

+ 333 - 0
src/store.js

@@ -0,0 +1,333 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+
+Vue.use(Vuex);
+const api = {
+  corpInfo: '/api/corp/corps/{corpid}/info',
+  corpBase: '/api/corp/corps/{corpid}',
+  hr: '/api/corp/corps/{corpid}/users', //list,add
+  hrImp: '/api/corp/users/{id}', //查询:id:手机号,用户肯定不知道id啊!那就手机号呗;修改:用id
+  corpIdentity: '/api/corp/corps/{corpid}/identity',
+  corpSchInfo: '/api/corp/corps/{corpid}/schs',
+  schs: '/api/corp/schs', //get:按学校查询入驻企业列表;post:申请入驻学校
+  profiles: '/api/jobs/profiles', //get查询招生简章列表;post创建招生简章
+  posts: '/api/jobs/posts',
+  postsinfo: '/api/jobs/posts/{id}',
+  profilesinfo: '/api/jobs/profiles/{id}',
+  infos: '/api/jobs/infos',
+  fairs: '/api/jobs/fairs',
+  fairsinfo: '/api/jobs/fairs/{id}',
+  faircorps: '/api/jobs/faircorps',
+  faircorpsInfo: '/api/jobs/faircorps/{id}',
+  infoss: '/api/jobs/infos/{id}',
+  talks: '/api/jobs/talks',
+  talksinfo: '/api/jobs/talks/{id}',
+  resumes: '/api/jobs/resumes',
+  letters: '/api/jobs/letters',
+  lettersinfo: '/api/jobs/letters/{id}',
+  userReg: '/api/stud/registers/{id}',
+  connection: '/weixin/qrcode/create',
+  wxtoken: '/weixin/qrcode/{qrcode}/token',
+  corpBind: '/api/corp/bind', //post=>query;corpid;body:mobile,wxtoken
+  corpLogin: '/api/corp/login',
+};
+
+export default new Vuex.Store({
+  state: {
+    user: {},
+  },
+  mutations: {
+    setUser(state, payload) {
+      if (payload) {
+        state.user = payload;
+        sessionStorage.setItem('user', JSON.stringify(payload));
+      } else {
+        let user = sessionStorage.getItem('user');
+        if (user) state.user = JSON.parse(user);
+        else return false;
+      }
+    },
+  },
+  actions: {
+    //用户信息
+    async userOperation({ state }, { type, data }) {
+      let result;
+      if (type === 'search') {
+        let { id } = data;
+        result = await this.$axios.$get(api.userReg, { id: id });
+      }
+      if (type === 'passwd') {
+        let { id, info } = data;
+        result = await this.$axios.$post(api.userReg, info, { id: id });
+      }
+      if (type === 'login') {
+        let { loginType, info } = data;
+        result = this.$axios.$post(api.corpLogin, info, {}, { type: loginType });
+      }
+      return result;
+    },
+    async corpSchInfo({ state, commit }, { type, data }) {
+      let result;
+      if (type === 'list') {
+        let { corpid, skip, limit } = data;
+        result = await this.$axios.$get(`${api.corpSchInfo}`, { corpid: corpid }, { skip: skip, limit: limit });
+      }
+
+      return result;
+    },
+    //宣讲会
+    async postTalksInfo({ state }, { type, data }) {
+      let { skip, limit } = data;
+      let result;
+      if (type === 'add') {
+        let { info, corpid, corpname, schid, schname } = data;
+        result = await this.$axios.$post(api.talks, info, {}, { schid: schid, corpid: corpid, corpname: corpname, schname: schname });
+      }
+      if (type === 'list') {
+        let { corpid, status } = data;
+        result = await this.$axios.$get(api.talks, {}, { corpid: corpid, status: status, skip: skip, limit: limit });
+      }
+      if (type === 'search') {
+        let { id } = data;
+        result = await this.$axios.$get(api.talksinfo, { id: id });
+      }
+      if (type === 'update') {
+        let { info, id } = data;
+        result = await this.$axios.$post(api.talksinfo, info, { id: id });
+      }
+      if (type === 'delete') {
+        let { id } = data;
+        result = await this.$axios.$delete(api.talksinfo, {}, { id: id });
+      }
+      return result;
+    },
+    //在线招聘
+    async jobinfoOperation({ state }, { type, data }) {
+      let { skip, limit } = data;
+      let result;
+      if (type === 'add') {
+        let { info, corpid, corpname, schid, schname } = data;
+        result = await this.$axios.$post(api.infos, info, {}, { schid: schid, corpid: corpid, corpname: corpname, schname: schname });
+      }
+      if (type === 'list') {
+        let { corpid, is_practice, status } = data;
+        result = await this.$axios.$get(api.infos, {}, { corpid: corpid, skip: skip, limit: limit, is_practice: is_practice, status: status });
+      }
+      if (type === 'search') {
+        let { id } = data;
+        result = await this.$axios.$get(api.infoss, { id: id });
+      }
+      if (type === 'update') {
+        let { info, id } = data;
+        result = await this.$axios.$post(api.infoss, info, { id: id });
+      }
+      if (type === 'delete') {
+        let { id } = data;
+        result = await this.$axios.$delete(api.infoss, {}, { id: id });
+      }
+      return result;
+    },
+    //职位管理
+    async postOperation({ state }, { type, data }) {
+      let { skip, limit } = data;
+      let result;
+      if (type === 'add') {
+        let { info, corpid, corpname } = data;
+        result = await this.$axios.$post(api.posts, info, {}, { corpid: corpid, corpname: corpname });
+      }
+      if (type === 'list') {
+        let { corpid, is_practice } = data;
+        result = await this.$axios.$get(`${api.posts}`, {}, { corpid: corpid, skip: skip, limit: limit, is_practice: is_practice });
+      }
+      if (type === 'search') {
+        let { id } = data;
+        result = await this.$axios.$get(api.postsinfo, { id: id });
+      }
+      if (type === 'update') {
+        let { info, id } = data;
+        result = await this.$axios.$post(api.postsinfo, info, { id: id });
+      }
+      if (type === 'delete') {
+        let { id } = data;
+        result = await this.$axios.$delete(api.postsinfo, {}, { id: id });
+      }
+      return result;
+    },
+    //hr一系列操作
+    async hrOperation({ state }, { type, data }) {
+      let result;
+      if (type === `delete`) {
+        let { id } = data;
+        result = await this.$axios.$delete(api.hrImp, {}, { id: id });
+      } else if (type === `add`) {
+        let { corpid, info } = data;
+        result = await this.$axios.$post(api.hr, info, { corpid: corpid });
+      } else if (type === 'search') {
+        let { tel } = data;
+        result = await this.$axios.$get(api.hrImp, { id: tel });
+      } else if (type === 'update') {
+        let { info } = data;
+        let { passwd, id } = info;
+        result = await this.$axios.$post(api.hrImp, { passwd: passwd }, { id: id });
+      } else {
+        result = await this.$axios.$get(api.hr, { corpid: data });
+      }
+      return result;
+    },
+    //招聘简章
+    async profilesOperation({ state }, { type, data }) {
+      let { skip, limit } = data;
+      let result;
+      if (type === 'add') {
+        let { info, corpid, corpname } = data;
+        result = await this.$axios.$post(api.profiles, info, {}, { corpid: corpid, corpname: corpname });
+      }
+      if (type === 'list') {
+        let { corpid } = data;
+        result = await this.$axios.$get(api.profiles, {}, { corpid: corpid, skip: skip, limit: limit });
+      }
+      if (type === 'search') {
+        let { id } = data;
+        result = await this.$axios.$get(api.profilesinfo, { id: id });
+      }
+      if (type === 'update') {
+        let { info, id } = data;
+        result = await this.$axios.$post(api.profilesinfo, info, { id: id });
+      }
+      if (type === 'delete') {
+        let { id } = data;
+        result = await this.$axios.$delete(api.profilesinfo, {}, { id: id });
+      }
+      return result;
+    },
+    //企业基本信息
+    async corpOperation({ state }, { type, data }) {
+      let result;
+      if (type === 'search') {
+        let { corpid } = data;
+        let info = await this.$axios.$get(`${api.corpInfo}`, { corpid: corpid });
+        let base = await this.$axios.$get(api.corpBase, { corpid: corpid });
+        let identity = await this.$axios.$get(api.corpIdentity, { corpid: corpid });
+        return { info: info.data, base: base.data, identity: identity.data };
+      }
+      if (type === 'info') {
+        let { corpid, info } = data;
+        result = await this.$axios.$post(api.corpInfo, info, { corpid: corpid });
+      }
+      if (type === 'identity') {
+        let { corpid, info } = data;
+        result = await this.$axios.$post(api.corpIdentity, info, { corpid: corpid });
+      }
+      return result;
+    },
+    //入驻学校
+    async joinSch({ state }, { schid, corpid }) {
+      let result = await this.$axios.$post(api.schs, { schid: schid, corpid: corpid });
+      return result;
+    },
+    // 简历管理
+    async resumesOperation({ state }, { type, data }) {
+      let result;
+      if (type === 'list') {
+        let { schid, year } = data;
+        result = await this.$axios.$get(api.resumes, {}, { schid: schid, year: year });
+      }
+      return result;
+    },
+    //求职信
+    async lettersOperation({ state }, { type, data }) {
+      let result;
+      let { skip, limit } = data;
+      if (type === 'list') {
+        let { resume_id, status, corpname, studname, post_id } = data;
+        result = await this.$axios.$get(
+          api.letters,
+          {},
+          { post_id: post_id, resume_id: resume_id, status: status, corpname: corpname, studname: studname, skip: skip, limit: limit }
+        );
+      }
+      if (type === 'search') {
+        let { id } = data;
+        result = await this.$axios.$get(api.lettersinfo, { id: id });
+      }
+      if (type === 'update') {
+        let { id, status } = data;
+        result = await this.$axios.$post(api.lettersinfo, { status: status }, { id: id });
+      }
+      return result;
+    },
+    // 招聘会
+    async jobfairOperation({ state }, { type, data }) {
+      let result;
+      if (type === 'add') {
+        let { info, corpid, corpname } = data;
+        result = await this.$axios.$post(api.fairs, info, {}, { corpid: corpid, corpname: corpname });
+      }
+      if (type === 'list') {
+        let { schid } = data;
+        result = await this.$axios.$get(api.fairs, {}, { schid: schid });
+      }
+      if (type === 'myList') {
+        let { schid, fairid, corpid, status } = data;
+        result = await this.$axios.$get(api.faircorps, {}, { schid: schid, fairid: fairid, corpid: corpid, status: status });
+      }
+      if (type === 'search') {
+        let { id } = data;
+        result = await this.$axios.$get(api.fairsinfo, { id: id });
+      }
+      if (type === 'update') {
+        let { info, id } = data;
+        result = await this.$axios.$post(api.faircorps, info, { id: id });
+      }
+      if (type === 'delete') {
+        let { id } = data;
+        result = await this.$axios.$delete(api.fairsinfo, {}, { id: id });
+      }
+      if (type === 'apply') {
+        let { schname, fairid, corpid, corpname, info } = data;
+        result = await this.$axios.$post(api.faircorps, info, {}, { schname: schname, fairid: fairid, corpid: corpid, corpname: corpname, schid: 999991 });
+      }
+      if (type === 'searchApply') {
+        let { id } = data;
+        result = await this.$axios.$get(api.faircorpsInfo, { id: id });
+      }
+      if (type === 'applyUpdate') {
+        let { id, info } = data;
+        result = await this.$axios.$post(api.faircorpsInfo, info, { id: id });
+      }
+      return result;
+    },
+    //微信部分
+    async createConnection() {
+      let result = await this.$axios.$post(api.connection);
+      if (result.errcode != undefined && result.errcode === 0) {
+        console.log('create qrcode success', result.data);
+        return result.data;
+      }
+      console.error('create qrcode fail', result);
+    },
+    async getWxtoken({ state }, qrcode) {
+      let result = await this.$axios.$post(api.wxtoken, {}, { qrcode: qrcode });
+      if (result.errcode != undefined && result.errcode === 0) {
+        console.log('qrcode login success', result);
+        return result.token;
+      }
+      console.error('create qrcode fail', result);
+    },
+    async bindOperation({ state }, { type, data }) {
+      let { corpid, ...info } = data;
+      let result = await this.$axios.$post(api.corpBind, info, {}, { corpid: corpid });
+      return result;
+    },
+  },
+});
+
+const data = {
+  testItem: {
+    id: Math.random(),
+    name: 'name',
+    age: 'age',
+    tel: '13099876544',
+  },
+};

+ 11 - 0
src/style/element.less

@@ -0,0 +1,11 @@
+.el-collapse-item__header {
+  height: 0;
+}
+.el-collapse-item__arrow{
+  margin: -10px 8px 0 auto;
+}
+//self
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}

+ 132 - 0
src/util/axios-wrapper.js

@@ -0,0 +1,132 @@
+/* eslint-disable no-console */
+/* eslint-disable no-param-reassign */
+
+import _ from 'lodash';
+import Axios from 'axios';
+import { Util, Error } from 'naf-core';
+// import { Indicator } from 'mint-ui';
+import util from './user-util';
+
+const { trimData, isNullOrUndefined } = Util;
+const { ErrorCode } = Error;
+
+let currentRequests = 0;
+
+export default class AxiosWrapper {
+  constructor({ baseUrl = '', unwrap = true } = {}) {
+    this.baseUrl = baseUrl;
+    this.unwrap = unwrap;
+  }
+
+  // 替换uri中的参数变量
+  static merge(uri, query = {}) {
+    if (!uri.includes(':')) {
+      return uri;
+    }
+    const keys = [];
+    const regexp = /\/:([a-z0-9_]+)/gi;
+    let res;
+    // eslint-disable-next-line no-cond-assign
+    while ((res = regexp.exec(uri)) != null) {
+      keys.push(res[1]);
+    }
+    keys.forEach(key => {
+      if (!isNullOrUndefined(query[key])) {
+        uri = uri.replace(`:${key}`, query[key]);
+      }
+    });
+    return uri;
+  }
+  //替换路由方法
+  static routerChange(uri, router = {}) {
+    let keys = Object.keys(router);
+    keys.forEach(key => {
+      uri = _.replace(uri, `{${key}}`, router[key]);
+    });
+    return uri;
+  }
+
+  $get(uri, router, query, options) {
+    return this.$request(uri, null, query, options, router);
+  }
+
+  $post(uri, data = {}, router, query, options) {
+    return this.$request(uri, data, query, options, router);
+  }
+
+  $delete(uri, data = {}, router, query, options = {}) {
+    options = { ...options, method: 'delete' };
+    return this.$request(uri, data, query, options, router);
+  }
+
+  async $request(uri, data, query, options, router) {
+    // TODO: 合并query和options
+    if (_.isObject(query) && _.isObject(options)) {
+      options = { ...options, params: query, method: 'get' };
+    } else if (_.isObject(query) && !query.params) {
+      options = { params: query };
+    } else if (_.isObject(query) && query.params) {
+      options = query;
+    }
+    if (!options) options = {};
+    if (options.params) options.params = trimData(options.params);
+    let url = AxiosWrapper.merge(uri, options.params);
+    //处理url部分需要替换参数的情况
+    if (_.isObject(router)) {
+      url = AxiosWrapper.routerChange(url, router);
+    }
+    console.log(url);
+    currentRequests += 1;
+    // Indicator.open({
+    //   spinnerType: 'fading-circle',
+    // });
+
+    try {
+      const axios = Axios.create({
+        baseURL: this.baseUrl,
+      });
+      axios.defaults.headers.common.Authorization = util.token;
+      let res = await axios.request({
+        method: isNullOrUndefined(data) ? 'get' : 'post',
+        url,
+        data,
+        responseType: 'json',
+        ...options,
+      });
+      res = res.data;
+      const { errcode, errmsg, details } = res;
+      if (errcode) {
+        console.warn(`[${uri}] fail: ${errcode}-${errmsg} ${details}`);
+        return res;
+      }
+      // unwrap data
+      if (this.unwrap) {
+        res = _.omit(res, ['errmsg', 'details']);
+        const keys = Object.keys(res);
+        if (keys.length === 1 && keys.includes('data')) {
+          res = res.data;
+        }
+      }
+      return res;
+    } catch (err) {
+      let errmsg = '接口请求失败,请稍后重试';
+      if (err.response) {
+        const { status } = err.response;
+        if (status === 401) errmsg = '用户认证失败,请重新登录';
+        if (status === 403) errmsg = '当前用户不允许执行该操作';
+      }
+      console.error(
+        `[AxiosWrapper] 接口请求失败: ${err.config && err.config.url} - 
+        ${err.message}`
+      );
+      return { errcode: ErrorCode.SERVICE_FAULT, errmsg, details: err.message };
+    } finally {
+      /* eslint-disable */
+      currentRequests -= 1;
+      if (currentRequests <= 0) {
+        currentRequests = 0;
+        // Indicator.close();
+      }
+    }
+  }
+}

+ 10 - 0
src/util/filters.js

@@ -0,0 +1,10 @@
+import _ from 'lodash';
+
+const filters = {
+  getName(object) {
+    const { data, searchItem } = object;
+    return _.get(data, searchItem) === undefined ? '' : _.get(data, searchItem);
+  },
+};
+
+export default filters;

+ 50 - 0
src/util/methods-util.js

@@ -0,0 +1,50 @@
+import { Util } from 'naf-core';
+
+const { isNullOrUndefined } = Util;
+
+export default {
+  //判断信息是否过期
+  isDateOff(dataDate) {
+    const now = new Date(new Date().getTime() - 24 * 60 * 60 * 1000);
+    dataDate = new Date(dataDate);
+    return now.getTime() <= dataDate.getTime();
+  },
+  //判断企业是否可以执行此动作/显示
+  checkCorp(data) {
+    const { role, unit, selfUnit, status, displayType, userid } = data;
+    if (!isNullOrUndefined(selfUnit) && !isNullOrUndefined(status)) {
+      return role === 'corp' && selfUnit === unit && status === '0';
+    } else if (!isNullOrUndefined(displayType)) {
+      if (role === 'corp') {
+        return role === displayType;
+      } else {
+        return role === displayType && !isNullOrUndefined(userid);
+      }
+    }
+  },
+  //获取url的参数params
+  getParams() {
+    let str = location.href;
+    let num = str.indexOf('?');
+    const param = {};
+    str = str.substr(num + 1);
+    let num2 = str.indexOf('#');
+    let str2 = '';
+    if (num2 > 0) {
+      str2 = str.substr(0, num2);
+    } else {
+      num2 = str.indexOf('/');
+      str2 = str.substr(0, num2);
+    }
+    const arr = str2.split('&');
+    for (let i = 0; i < arr.length; i++) {
+      num = arr[i].indexOf('=');
+      if (num > 0) {
+        const name = arr[i].substring(0, num);
+        const value = arr[i].substr(num + 1);
+        param[name] = decodeURI(value);
+      }
+    }
+    return param;
+  },
+};

+ 47 - 0
src/util/optionTitles.js

@@ -0,0 +1,47 @@
+export const JOBFAIR_TITLE = [
+  { prop: 'subject', label: '' },
+  { prop: 'address', label: '举办地址' },
+  { prop: 'date', label: '举办日期' },
+  { prop: 'unit', label: '分站信息' },
+];
+
+export const CAMPUS_TITLE = [
+  { prop: 'subject', label: '' },
+  { prop: 'address', label: '举办地址' },
+  { prop: 'status', label: '审核状态' },
+  { prop: 'date', label: '举办日期' },
+  { prop: 'unit', label: '分站信息' },
+];
+
+export const JOBINFO_TITLE = [
+  { prop: 'title', label: '' },
+  { prop: 'count', label: '需求人数' },
+  { prop: 'nature.name', label: '工作性质' },
+  { prop: 'salary.name', label: '薪资待遇' },
+  { prop: 'xlreqs.name', label: '最低学历' },
+  { prop: 'city.name', label: '所在城市' },
+  // { prop: 'expired', label: '状态' },
+];
+
+export const RESUME_TITLE = [{ prop: 'title', label: '' }];
+
+export const LETTER_TITLE = [
+  { prop: 'title', label: '' },
+  { prop: 'corpname', label: '企业名称' },
+  { prop: 'type', label: '类型' },
+  { prop: 'status', label: '状态' },
+];
+
+export const TICKET_TITLE = [
+  { prop: 'subject', label: '' },
+  { prop: 'type', label: '门票类型' },
+  { prop: 'origin', label: '' },
+  { prop: 'date', label: '举办日期' },
+];
+
+export const CORP_JOBFAIR = [
+  { prop: 'subject', label: '' },
+  { prop: 'time', label: '举办时间' },
+  { prop: 'date', label: '举办日期' },
+  { prop: 'unit', label: '分站信息' },
+];

+ 96 - 0
src/util/qrcode.vue

@@ -0,0 +1,96 @@
+<template>
+  <div id="qrcodes" style="width:100%;">
+    <mt-header title="二维码">
+      <mt-button class="bgnone" slot="left" @click="$router.go(-1)">返回</mt-button>
+    </mt-header>
+    <span v-if="user.role === 'user'">
+      <li class="txtQr" style="padding-top:7vh;">学生姓名:{{ userInfo.xm || '' }}</li>
+      <li class="txtQr">门票类型:{{ ticketType }}</li>
+    </span>
+    <div id="qrcode" style="display:flex;justify-content:center;align-items:center;" :style="newHeight" ref="qrcode">
+      <canvas id="canvas" style="display:-webkit-inline-box;width: 4rem !important;height:4rem !important;margin-top: 200px;"></canvas>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapActions, mapState, mapMutations } from 'vuex';
+import QRCode from 'qrcode';
+export default {
+  name: 'qrcodes',
+  data() {
+    return {
+      id: this.$route.query.id || '',
+      ticketType: this.$route.query.type && this.$route.query.type === '0' ? '普通票' : '受限票' || '',
+      popupVisible: false,
+    };
+  },
+  computed: {
+    ...mapState({
+      user: state => state.publics.user,
+      userInfo: state => state.self.userInfo,
+    }),
+    newHeight: {
+      get() {
+        let height;
+        if (this.user.role === 'user') {
+          height = window.screen.availHeight * 0.3 + 'px';
+        } else {
+          height = window.screen.availHeight * 0.6 + 'px';
+        }
+        let style = { height: height };
+        return style;
+      },
+    },
+  },
+  created() {
+    this.$nextTick(() => {
+      this.initQrcode();
+    });
+  },
+  methods: {
+    async initQrcode() {
+      if (!this.booForQrcode) {
+        await QRCode.toCanvas(document.getElementById('canvas'), this.id, {
+          width: 300,
+          margin: 0,
+          color: { dark: this.$route.query.type === '0' ? '#00ff14' : '#FF9900' },
+        });
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.txtQr {
+  font-size: 14px;
+  text-align: center;
+  min-height: 30px;
+  margin-bottom: 1px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.mint-header {
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  background-color: #26a2ff;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  color: #fff;
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  height: 50px;
+  line-height: 50px;
+  color: #fff;
+  font-size: 16px;
+  background: #2577e3;
+  padding: 0;
+  position: relative;
+  text-align: center;
+  white-space: nowrap;
+}
+</style>

+ 69 - 0
src/util/user-util.js

@@ -0,0 +1,69 @@
+/* eslint-disable no-console */
+export default {
+  get user() {
+    const val = sessionStorage.getItem('user');
+    try {
+      if (val) return JSON.parse(val);
+    } catch (err) {
+      console.error(err);
+    }
+    return null;
+  },
+  set user(userinfo) {
+    sessionStorage.setItem('user', JSON.stringify(userinfo));
+  },
+  get token() {
+    return sessionStorage.getItem('token');
+  },
+  set token(token) {
+    sessionStorage.setItem('token', token);
+  },
+  get openid() {
+    return sessionStorage.getItem('openid');
+  },
+  set openid(openid) {
+    sessionStorage.setItem('openid', openid);
+  },
+  get isGuest() {
+    return !this.user || this.user.role === 'guest';
+  },
+  save({ userinfo, token }) {
+    sessionStorage.setItem('user', JSON.stringify(userinfo));
+    sessionStorage.setItem('token', token);
+  },
+
+  get corpInfo() {
+    const val = sessionStorage.getItem('corpInfo');
+    if (val) return JSON.parse(val);
+    return null;
+  },
+  set corpInfo(corpInfo) {
+    sessionStorage.setItem('corpInfo', JSON.stringify(corpInfo));
+  },
+  saveCorpInfo(corpInfo) {
+    sessionStorage.setItem('corpInfo', JSON.stringify(corpInfo));
+  },
+
+  get unit() {
+    const val = sessionStorage.getItem('unit');
+    if (val) return JSON.parse(val);
+    return null;
+  },
+  set unit(unitList) {
+    sessionStorage.setItem('unit', JSON.stringify(unitList));
+  },
+  saveUnit(unitList) {
+    sessionStorage.setItem('unit', JSON.stringify(unitList));
+  },
+  get userInfo() {
+    const val = sessionStorage.getItem('userInfo');
+    if (val) return JSON.parse(val);
+    return null;
+  },
+  set userInfo(userInfo) {
+    sessionStorage.setItem('userInfo', JSON.stringify(userInfo));
+  },
+  saveUserInfo(userInfo) {
+    sessionStorage.setItem('userInfo', JSON.stringify(userInfo));
+  },
+};

+ 367 - 0
src/views/home/index.vue

@@ -0,0 +1,367 @@
+<template>
+  <div id="home">
+    <el-row :span="24" class="tit">
+      <h4>企业工作台</h4>
+    </el-row>
+    <el-row class="homeMain">
+      <el-col class="homeEnr">
+        <div style="font-size:20px;">
+          校招
+        </div>
+        <ul class="homeEnrUl">
+          <li>
+            <a href="">
+              <span>
+                <em class="number number_s">2</em>
+                <strong class="title">入驻学校</strong>
+              </span>
+            </a>
+          </li>
+        </ul>
+      </el-col>
+      <el-col class="homeRec">
+        <div style="font-size:20px;">
+          招聘
+        </div>
+        <ul class="homeEnrUl homeRecUl">
+          <li>
+            <a href="">
+              <span>
+                <em class="number number_s">5</em>
+                <strong class="title">招聘职位数</strong>
+              </span>
+            </a>
+          </li>
+          <li>
+            <a href="">
+              <span>
+                <em class="number number_s">60</em>
+                <strong class="title">职位阅读数</strong>
+              </span>
+            </a>
+          </li>
+          <li>
+            <a href="">
+              <span>
+                <em class="number number_s">4</em>
+                <strong class="title">接受简历数</strong>
+              </span>
+            </a>
+          </li>
+          <li>
+            <a href="">
+              <span>
+                <em class="number number_s">4</em>
+                <strong class="title">企业人才库</strong>
+              </span>
+            </a>
+          </li>
+          <li>
+            <a href="">
+              <span>
+                <em class="number number_s">0</em>
+                <strong class="title">微信关注数</strong>
+              </span>
+            </a>
+          </li>
+        </ul>
+      </el-col>
+    </el-row>
+    <el-row class="homeRes">
+      <el-col class="homeResTop">
+        <h4>最新简历</h4>
+        <a href="">换一换</a>
+      </el-col>
+      <ul class="homeResMain">
+        <li v-for="(tag, index) in tags" :key="index" :type="tag.type" @click="checkResume(tag.id)">
+          <el-col class="homeResMainImg">
+            <img src="../../assets/homejianli.png" width="80" height="80" />
+          </el-col>
+          <el-col class="homeResMainTit">
+            <p class="text-owt">{{ tag.name }}</p>
+            <p class="text-owt">专业:{{ tag.major }}</p>
+            <p class="text-owt">求职意向:{{ tag.intention }}</p>
+          </el-col>
+        </li>
+      </ul>
+    </el-row>
+    <el-row class="homeMessage">
+      <el-col class="homeResTop">
+        <h4>系统消息</h4>
+        <a href=""></a>
+      </el-col>
+      <ul class="homeMessageMain">
+        <li v-for="(tag, index) in tagsSys" :key="index" :type="tag.type">
+          <el-col class="MessageMainTit text-owt">
+            {{ tag.message }}
+          </el-col>
+          <el-col class="MessageMainDay text-owt">
+            {{ tag.day }}
+          </el-col>
+        </li>
+      </ul>
+    </el-row>
+    <el-dialog title="简历详情" center :visible.sync="resumeDialog" :fullscreen="true">
+      <resumes></resumes>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import resumes from '@resume/src/views/resumeCor.vue';
+export default {
+  name: 'home',
+  metaInfo: {
+    title: '企业主页',
+  },
+  components: {
+    resumes,
+  },
+  data: () => ({
+    resumeDialog: false,
+    tags: [
+      { name: '陈宁宁  女 | 本科 | 长春大学', major: '英语', intention: '市场营销,教师,销售 | ,深圳,上海,南京,温州 | 5K' },
+      { name: '陈宁宁  女 | 本科 | 长春大学', major: '英语', intention: '市场营销,教师,销售 | ,深圳,上海,南京,温州 | 5K' },
+      { name: '陈宁宁  女 | 本科 | 长春大学', major: '英语', intention: '市场营销,教师,销售 | ,深圳,上海,南京,温州 | 5K' },
+      { name: '陈宁宁  女 | 本科 | 长春大学', major: '英语', intention: '市场营销,教师,销售 | ,深圳,上海,南京,温州 | 5K' },
+      { name: '陈宁宁  女 | 本科 | 长春大学', major: '英语', intention: '市场营销,教师,销售 | ,深圳,上海,南京,温州 | 5K' },
+      { name: '陈宁宁  女 | 本科 | 长春大学', major: '英语', intention: '市场营销,教师,销售 | ,深圳,上海,南京,温州 | 5K' },
+    ],
+    tagsSys: [
+      { message: '【在线招聘】您申请的在线招聘:"fffffff,已通过审批', day: '2019-07-02 16:05' },
+      { message: '【在线招聘】您申请的在线招聘:"fffffff,已通过审批', day: '2019-07-02 16:05' },
+      { message: '【在线招聘】您申请的在线招聘:"fffffff,已通过审批', day: '2019-07-02 16:05' },
+      { message: '【在线招聘】您申请的在线招聘:"fffffff,已通过审批', day: '2019-07-02 16:05' },
+      { message: '【在线招聘】您申请的在线招聘:"fffffff,已通过审批', day: '2019-07-02 16:05' },
+      { message: '【在线招聘】您申请的在线招聘:"fffffff,已通过审批', day: '2019-07-02 16:05' },
+      { message: '【在线招聘】您申请的在线招聘:"fffffff,已通过审批', day: '2019-07-02 16:05' },
+    ],
+  }),
+  created() {},
+  methods: {
+    checkResume(id) {
+      this.resumeDialog = true;
+    },
+  },
+};
+</script>
+<style lang="less" scoped>
+/deep/.el-dialog__body {
+  padding: 0;
+}
+.tit h4 {
+  color: #333;
+  font-size: 15px;
+  font-weight: 500;
+  margin: 0;
+  margin: 20px 0;
+}
+ul {
+  margin: 0;
+  padding: 0;
+}
+li {
+  list-style: none;
+}
+a {
+  text-decoration: none;
+}
+p {
+  margin: 0;
+  padding: 0;
+}
+h4 {
+  margin: 0;
+}
+.text-owt {
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
+.homeMain {
+  float: left;
+  width: 100%;
+  height: 140px;
+}
+.homeEnr {
+  text-align: center;
+  width: 18%;
+  display: inline-block;
+  background-color: #7cbae5;
+  border: 1px solid #6eb0dd;
+  color: #fff;
+  margin-right: 19px;
+}
+.homeEnrUl {
+  overflow: hidden;
+  display: block;
+  padding: 0;
+  padding-bottom: 26px;
+  margin: 0;
+}
+.homeEnrUl li {
+  float: left;
+  display: block;
+  color: #fff;
+  width: 100%;
+  height: 75px;
+}
+.homeRecUl li {
+  float: left;
+  display: block;
+  color: #fff;
+  width: 20%;
+  height: 75px;
+}
+.homeEnrUl li a {
+  color: #fff;
+}
+.homeEnrUl li a span {
+  display: block;
+  border-left: 1px solid #6fa7ce;
+}
+.homeRecUl li a span {
+  display: block;
+  border-left: 1px solid #57c78b;
+}
+.homeEnrUl li a span .number {
+  margin-left: 5px;
+  font-weight: 400;
+  font-style: normal;
+  vertical-align: middle;
+  font-size: 35px;
+  line-height: 60px;
+}
+.homeEnrUl li a span .number_s {
+  font-size: 16px;
+}
+.homeEnrUl li a span .title {
+  display: block;
+  font-weight: 400;
+  font-style: normal;
+  font-size: 16px;
+  letter-spacing: 2px;
+  margin-top: -10px;
+}
+.homeRec {
+  text-align: center;
+  width: 79%;
+  display: inline-block;
+  background-color: #60d295;
+  border: 1px solid #60d295;
+  color: #fff;
+  margin-bottom: 15px;
+}
+.homeRes {
+  float: left;
+  width: 100%;
+  border: 1px solid #ccc;
+  margin: 0 0 20px 0;
+}
+.homeResTop {
+  width: 100%;
+  height: 39px;
+  background: #f4f5fa;
+  border-bottom: 1px solid #ccc;
+}
+.homeResTop h4 {
+  float: left;
+  width: 50%;
+  padding: 10px;
+  font-size: 14px;
+  color: #333;
+  font-weight: 500;
+}
+.homeResTop a {
+  float: right;
+  text-align: right;
+  width: 45%;
+  padding: 10px;
+  font-size: 14px;
+  color: #337ab7;
+}
+.homeResTop a:hover {
+  color: #23527c;
+  text-decoration: underline;
+}
+.homeResMain {
+  float: left;
+  width: 100%;
+  height: 387px;
+  overflow: hidden;
+}
+.homeResMain li {
+  float: left;
+  width: 452px;
+  height: 87px;
+  padding: 20px 8px 20px 20px;
+  color: #333;
+  font-size: 14px;
+  background: #fff;
+  border-top: 1px solid #ccc;
+  border-bottom: 1px solid #ccc;
+}
+.homeResMain li:hover {
+  background: #f5f5f5;
+}
+.homeResMainImg {
+  float: left;
+  width: 80px;
+  height: 80px;
+  border-radius: 90px;
+}
+.homeResMainImg img {
+  border-radius: 90px;
+}
+.homeResMainTit {
+  float: left;
+  width: 350px;
+  height: 88px;
+  padding: 0 0 0 15px;
+}
+.homeResMainTit p {
+  font-size: 14px;
+  color: #333;
+  line-height: 22px;
+}
+
+.homeMessage {
+  float: left;
+  width: 100%;
+  border: 1px solid #ccc;
+}
+.homeMessageMain {
+  float: left;
+  width: 100%;
+  height: 246px;
+  overflow: hidden;
+}
+.homeMessageMain li {
+  float: left;
+  width: 100%;
+  height: 39px;
+  border-top: 1px solid #ccc;
+  border-bottom: 1px solid #ccc;
+  background: #fff;
+}
+.homeMessageMain li:hover {
+  background: #f5f5f5;
+}
+.homeMessageMain li .MessageMainTit {
+  float: left;
+  width: 763px;
+  height: 39px;
+  padding: 10px;
+  font-size: 14px;
+  color: #333;
+  font-weight: 500;
+}
+.homeMessageMain li .MessageMainDay {
+  float: right;
+  width: 140px;
+  padding: 10px;
+  font-size: 14px;
+  color: #999;
+  font-weight: 500;
+}
+</style>

+ 196 - 0
src/views/info/base/auth.vue

@@ -0,0 +1,196 @@
+<template>
+  <div id="auth">
+    <el-row>
+      <el-form :model="identitys" :rules="rules" ref="form">
+        <form-item label="所属行业" type="select" prop="industry">
+          <el-select v-model="identity.industry" placeholder="请选择所属行业">
+            <el-option v-for="(item, index) in selectList1" :key="index" :label="item.label" :value="item.label"></el-option>
+          </el-select>
+        </form-item>
+        <form-item label="单位性质" prop="nature">
+          <el-select v-model="identity.nature" placeholder="请选择单位性质">
+            <el-option v-for="(item, index) in selectList2" :key="index" :label="item.label" :value="item.label"></el-option>
+          </el-select>
+        </form-item>
+        <form-item label="注册资金" prop="reg_capital">
+          <el-input v-model="identity.reg_capital" placeholder="请输入注册资金" :disabled="false"> </el-input>
+        </form-item>
+        <form-item label="信用代码" prop="org_code">
+          <el-input v-model="identity.org_code" placeholder="统一社会信用代码(或组织机构代码)" :disabled="false"> </el-input>
+        </form-item>
+        <form-item label="单位标签(选填)" prop="tags">
+          <el-select v-model="identity.tags" placeholder="请选择单位标签" multiple>
+            <el-option v-for="(item, index) in selectList3" :key="index" :label="item.label" :value="item.label"></el-option>
+          </el-select>
+        </form-item>
+        <form-item label="企业LOGO">
+          <el-row>
+            <el-col :span="24">
+              <span class="tip">最大支持100k </span>
+            </el-col>
+            <el-col :span="24">
+              <upload ref="logo" :data="imgs.logo_url" :limit="1" :url="`/files/corp/identity/upload`" type="logo_url" @upload="uploadSuccess"></upload>
+            </el-col>
+          </el-row>
+        </form-item>
+        <form-item label="企业资料">
+          <el-row>
+            <el-col :span="24">
+              <span class="tip">1、每张最大支持2M</span>
+            </el-col>
+            <el-col :span="24">
+              <span class="tip">2、组织机构代码证,必填:三证合一,请在此上传,如果是老证,请上传组织机构代码证</span>
+            </el-col>
+            <el-col :span="24">
+              <span class="tip">3、请务必上传带彩色公章的企业资料(扫描彩色件或原件)图片</span>
+            </el-col>
+            <el-col :span="24">
+              营业执照
+            </el-col>
+            <el-col :span="24">
+              <upload type="yyzz" :data="imgs.yyzz" :limit="1" :url="`/files/corp/identity/upload`" @upload="uploadSuccess"></upload>
+            </el-col>
+            <el-col :span="24">
+              组织机构代码
+            </el-col>
+            <el-col :span="24">
+              <upload type="zzjgdm" :data="imgs.zzjgdm" :limit="1" :url="`/files/corp/identity/upload`" @upload="uploadSuccess"></upload>
+            </el-col>
+          </el-row>
+        </form-item>
+      </el-form>
+      <el-row type="flex" justify="center">
+        <el-col :span="6">
+          <el-button type="success" style="width:60%" @click="onSubmits">保&nbsp;&nbsp;&nbsp;&nbsp;存</el-button>
+        </el-col>
+      </el-row>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import upload from '@/components/upload.vue';
+import formItem from '@/components/form-item.vue';
+import { log } from 'util';
+export default {
+  name: 'auth',
+  props: {
+    original: { type: Object, default: () => {} },
+    identitys: { type: Object, default: () => {} },
+  },
+  components: {
+    formItem,
+    upload,
+  },
+  data: () => ({
+    identity: {
+      tags: [],
+    },
+    imgs: {},
+    corp2: {},
+    selectList1: [
+      { label: '农、林、牧、渔业', value: '1' },
+      { label: '采矿业', value: '2' },
+      { label: '制造业', value: '3' },
+      { label: '电力、热力、燃气及水生产和供应业', value: '4' },
+      { label: '建筑业"', value: '5' },
+      { label: '批发和零售业', value: '6' },
+      { label: '交通运输、仓储和邮政业', value: '7' },
+      { label: '住宿和餐饮业', value: '2' },
+      { label: '信息传输,软件和信息技术服务', value: '8' },
+      { label: '金融业', value: '9' },
+      { label: '房地产业', value: '10' },
+      { label: '租赁和商务服务业', value: '11' },
+      { label: '科学研究和技术服务业', value: '12' },
+      { label: '水利、环境和公共设施管理业', value: '13' },
+      { label: '居民服务、修理和其他服务业', value: '14' },
+      { label: '教育', value: '15' },
+      { label: '卫生和社会工作', value: '16' },
+      { label: '文化、体育和娱乐业', value: '17' },
+      { label: '公共管理、社会保障和社会组织', value: '18' },
+      { label: '国际组织', value: '19' },
+      { label: '军队', value: '20' },
+    ],
+    selectList2: [
+      { label: '机关', value: '1' },
+      { label: '科研设计单位', value: '2' },
+      { label: '高等教育单位', value: '3' },
+      { label: '中初教育单位', value: '4' },
+      { label: '医疗卫生单位', value: '5' },
+      { label: '其他事业单位', value: '6' },
+      { label: '民营企业', value: '7' },
+      { label: '三资企业', value: '8' },
+      { label: '其他企业', value: '9' },
+      { label: '部队', value: '10' },
+      { label: '农村建制村', value: '11' },
+      { label: '城镇社区', value: '12' },
+      { label: '其他', value: '13' },
+    ],
+    selectList3: [
+      { label: '世界500强', value: '1' },
+      { label: '中国100强', value: '2' },
+      { label: '中国500强', value: '3' },
+      { label: '民企500强', value: '4' },
+      { label: '行业500强', value: '5' },
+      { label: '上市公司', value: '6' },
+    ],
+    rules: {
+      value1: [{ required: true, message: '选择测试', trigger: 'change' }],
+      // value3: [{ required: true, message: '输入测试', trigger: 'blur' }],
+    },
+  }),
+  created() {},
+  watch: {
+    identitys: {
+      handler(val) {
+        if (val.ident_pics.length > 0) {
+          val.ident_pics.map(item => {
+            // let url = item.name.replace('/files', 'http://smart.cc-lotus.info/files');
+            // console.log(url);
+            this.imgs[item.title] = item.name;
+          });
+        }
+        this.$set(this, `identity`, val);
+        if (val.logo_url) {
+          this.imgs['logo_url'] = val.logo_url;
+        }
+      },
+    },
+  },
+  mounted() {},
+  computed: {},
+  methods: {
+    onSubmits() {
+      let keys = Object.keys(this.imgs);
+      let arr = [];
+      keys.map(key => {
+        if (key === 'logo_url') {
+          this.identity.logo_url = this.imgs[key];
+        } else {
+          arr.push({ name: this.imgs[key], title: key });
+        }
+      });
+      this.identity.ident_pics = arr;
+      console.log(this.identity);
+      this.$emit(`onSubmit`, { data: this.identity, type: 'identity' });
+    },
+    uploadSuccess({ type, data }) {
+      // if (type === 'logo_url') {
+      //   console.log(data);
+      //   this.$set(this.identity, type, data.uri);
+      // } else {
+      this.imgs[type] = data.uri;
+      //}
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.tip {
+  color: #999999;
+}
+.el-select {
+  width: 100%;
+}
+</style>

+ 72 - 0
src/views/info/base/index.vue

@@ -0,0 +1,72 @@
+<template>
+  <div id="index">
+    <detail-layout>
+      <template v-slot:title>
+        基本资料
+      </template>
+      <template v-slot:main>
+        <!-- 下面3个标签页各自独立,由此组件发送请求后再将数据传给子组件,供其显示与修改;
+        子组件在通过提供的方法将更改后的信息传回,提交;
+        流程:请求数据=>子组件=>子组件做副本,用来显示和修改=>修改=>将数据传回父组件,只提交该子组件负责的范围内数据 -->
+        <el-tabs v-model="activeName" type="card">
+          <el-tab-pane label="基本资料" name="first">
+            <info :original="info" :base="base" :identity="identity" @onSubmit="onSubmit"></info>
+          </el-tab-pane>
+          <el-tab-pane label="认证信息" name="second">
+            <auth :base="base" :identitys="identity" @onSubmit="onSubmit"></auth>
+          </el-tab-pane>
+        </el-tabs>
+      </template>
+    </detail-layout>
+  </div>
+</template>
+
+<script>
+import info from './info.vue';
+import auth from './auth.vue';
+import detailLayout from '@/layout/detail-layout.vue';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  metaInfo: {
+    title: '企业基本资料',
+  },
+  props: {},
+  components: {
+    detailLayout,
+    info,
+    auth,
+  },
+  data: () => ({
+    activeName: 'first',
+    info: {},
+    base: {},
+    identity: {},
+  }),
+  created() {
+    this.getInfo();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['corpOperation', 'getCorpInfo', 'postCorpInfo']),
+    async getInfo() {
+      let { info, base, identity } = await this.corpOperation({ type: 'search', data: { corpid: this.user.corpid } });
+      this.$set(this, 'info', info);
+      this.$set(this, 'base', base);
+      this.$set(this, 'identity', identity);
+    },
+    async onSubmit({ data, type }) {
+      //连接后台接口
+      let result = await this.corpOperation({ data: { corpid: this.user.corpid, info: data }, type: type });
+      this.$message({
+        type: `${result.errcode}` === '0' ? 'success' : 'error',
+        message: `${result.errcode}` === '0' ? '修改成功' : result.errmsg,
+      });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 154 - 0
src/views/info/base/info.vue

@@ -0,0 +1,154 @@
+<template>
+  <div id="info">
+    <el-row>
+      <el-form :model="info">
+        <form-item label="认证状态">
+          <!-- <span :style="`color:${info.state === 0 ? 'green' : 'red'}`">{{ info.state === 0 ? '已认证' : '认证失败' }}-->
+          {{ corp.state }}
+        </form-item>
+        <form-item label="企业名称" :disabled="false" prop="company_name" placeholder="请输入企业名称">
+          {{ corp.corpname }}
+        </form-item>
+        <form-item label="HR管理员">
+          {{ corp.hr }}
+        </form-item>
+        <form-item label="所在省份" prop="province">
+          <el-select v-model="info.province" filterable placeholder="请选择所在省份">
+            <el-option v-for="(item, index) in provinceList" :key="index" :label="item.label" :value="item.label"></el-option>
+          </el-select>
+        </form-item>
+        <form-item label="所在城市" prop="city">
+          <el-select v-model="info.city" filterable placeholder="请选择所在城市">
+            <el-option v-for="(item, index) in cityList" :key="index" :label="item.label" :value="item.label"></el-option>
+          </el-select>
+        </form-item>
+        <form-item label="单位地址" prop="company_name">
+          <el-input v-model="info.address" placeholder="请输入单位地址"> {{ info.address }}</el-input>
+        </form-item>
+        <form-item label="单位规模" prop="scale">
+          <el-select v-model="info.scale" filterable placeholder="单位规模">
+            <el-option v-for="(item, index) in scaleList" :key="index" :label="item.label" :value="item.label"></el-option>
+          </el-select>
+        </form-item>
+        <form-item label="固定电话" prop="company_name">
+          <el-input v-model="info.address" placeholder="请输入固定电话"> {{ info.tel }}</el-input>
+        </form-item>
+        <form-item label="招聘电话" prop="company_name">
+          <el-input v-model="info.job_tel" placeholder="请输入招聘电话"> {{ info.job_tel }}</el-input>
+        </form-item>
+        <form-item label="招聘邮箱" prop="company_name">
+          <el-input v-model="info.job_email" placeholder="请输入招聘邮箱"> {{ info.job_email }}</el-input>
+        </form-item>
+        <form-item label="企业邮箱" prop="company_name">
+          <el-input v-model="info.email" placeholder="请输入企业邮箱"> {{ info.email }}</el-input>
+        </form-item>
+        <!-- <form-item v-model="info.company_email" label="企业愿景" :disabled="false" prop="company_name" placeholder="请输入企业愿景"></form-item>
+        <form-item v-model="info.logo_url" label="网申网址" :disabled="false" prop="company_name" placeholder="请输入网申网址"></form-item>
+        <form-item v-model="info.video_url" label="视频网址" :disabled="false" prop="video_url" placeholder="请输入视频网址"></form-item>
+        <form-item v-model="info.h5_url" label="H5宣传页" :disabled="false" prop="company_name" placeholder="请输入H5宣传页"></form-item>
+        <form-item label="企业相册" >
+          <template >
+            <upload ref="picUpload"></upload>
+          </template>
+        </form-item> -->
+        <form-item label="企业介绍">
+          <template>
+            <el-row>
+              <el-col :span="24">
+                <span class="tip">(请输入企业介绍,400字以内)</span>
+                <span class="danger">请勿输入薪资面议及其他外链</span>
+              </el-col>
+              <el-col :span="24">
+                <el-input
+                  v-model="info.intro"
+                  type="textarea"
+                  :autosize="{ minRows: 9, maxRows: 10 }"
+                  :maxlength="400"
+                  :show-word-limit="true"
+                  placeholder="请勿输入薪资面议及其他外链"
+                ></el-input>
+              </el-col>
+            </el-row>
+          </template>
+        </form-item>
+      </el-form>
+      <el-row type="flex" justify="center">
+        <el-col :span="6">
+          <el-button type="success" style="width:60%" @click="onSubmit">保&nbsp;&nbsp;&nbsp;&nbsp;存</el-button>
+        </el-col>
+      </el-row>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import upload from '@/components/upload.vue';
+import formItem from '@/components/form-item.vue';
+export default {
+  name: 'info',
+  props: {
+    original: { type: Object, default: () => {} },
+    base: { type: Object, default: () => {} },
+  },
+  components: {
+    formItem,
+    // upload,
+  },
+  data: () => ({
+    info: {},
+    corp: {},
+    provinceList: [
+      {
+        label: '北京',
+        value: '1',
+      },
+      {
+        label: '上海',
+        value: '2',
+      },
+      {
+        label: '深圳',
+        value: '3',
+      },
+      {
+        label: '吉林',
+        value: '4',
+      },
+    ],
+    cityList: [{ label: '长春', value: '1' }, { label: '吉林', value: '2' }, { label: '榆树', value: '3' }, { label: '北京', value: '4' }],
+    scaleList: [{ label: '少于50人', value: `1` }, { label: '50-100人', value: `2` }, { label: '100-150人', value: `3` }, { label: '300人以上', value: `4` }],
+  }),
+  created() {},
+  watch: {
+    original: {
+      handler(val) {
+        this.$set(this, `info`, val);
+      },
+    },
+    base: {
+      handler(val) {
+        this.$set(this, `corp`, val);
+      },
+    },
+  },
+  mounted() {},
+  computed: {},
+  methods: {
+    uploadPic() {
+      console.log(this.$refs.picUpload.$refs.upload);
+    },
+    onSubmit() {
+      this.$emit(`onSubmit`, { data: this.info, type: 'info' });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.tip {
+  color: #999999;
+}
+.danger {
+  color: #ff0000;
+}
+</style>

+ 0 - 0
src/views/info/bind/index.vue


+ 241 - 0
src/views/info/hr/index.vue

@@ -0,0 +1,241 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template v-slot:title>
+        HR成员
+      </template>
+      <template v-slot:functionBar>
+        <el-col :span="6">
+          &nbsp;
+          <!-- <search v-model="input" placeholder="请输入手机号查询HR"></search> -->
+        </el-col>
+        <el-col :span="17">
+          &nbsp;
+          <!-- <el-button type="info" circle icon="el-icon-search icons" @click="search()"></el-button> -->
+        </el-col>
+        <el-col :span="1">
+          <el-button type="success" @click="openAlert('add')">新增HR</el-button>
+        </el-col>
+      </template>
+      <template v-slot:main>
+        <el-table :data="dataList" stripe border style="width: 100%">
+          <el-table-column align="center" type="index" label="序号" width="50"> </el-table-column>
+          <el-table-column align="center" prop="name" label="HR姓名" width="200"></el-table-column>
+          <el-table-column align="center" prop="mobile" label="联系电话" width="200"> </el-table-column>
+          <el-table-column align="center" prop="operation" label="操作">
+            <template v-slot="scoped">
+              <el-row :gutter="1" class="row-bg" type="flex" align="middle" justify="center">
+                <el-col :span="8">
+                  <el-button size="mini" type="text" @click="openAlert('update', scoped.row)">修改密码</el-button>
+                </el-col>
+                <el-col :span="8">
+                  <el-button size="mini" type="text" @click="toBind(scoped.row)">更绑微信</el-button>
+                </el-col>
+                <el-col :span="8">
+                  <el-button size="mini" type="text" @click="toDelete(scoped.row)">注销用户</el-button>
+                </el-col>
+              </el-row>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <el-row class="row_pagination">
+          <el-col :span="24">
+            <el-pagination
+              @current-change="handleCurrentChange"
+              :current-page="currentPage"
+              :page-size="15"
+              layout="total, prev, pager, next, jumper"
+              :total="totalRow"
+            >
+            </el-pagination>
+          </el-col>
+        </el-row>
+        <el-dialog :title="dialogTitle" :visible.sync="dialog" @close="closeAlert">
+          <el-row type="flex" align="middle" justify="center">
+            <el-col :span="dialogWidth">
+              <el-form :model="form" label-width="auto" label-position="right">
+                <el-col :span="24">
+                  <el-form-item label="HR姓名" porp="name">
+                    <el-input v-model="form.name" style="width: 100%" v-if="!dialogTitle.includes('修改')"></el-input>
+                    <span v-else>{{ form.name }}</span>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                  <el-form-item label="手机" porp="mobile" v-if="!dialogTitle.includes('修改')">
+                    <el-input v-model="form.mobile" style="width: 100%"></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                  <el-form-item label="密码" porp="passwd">
+                    <el-input v-model="form.passwd" type="password" show-password style="width: 100%"></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                  <el-form-item label="图形码" porp="pic" v-if="!dialogTitle.includes('修改')">
+                    <el-input v-model="noUse.pic" style="width: 100%"></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="24" v-if="!dialogTitle.includes('修改')">
+                  <el-col :span="15">
+                    <el-form-item label="验证码" porp="data">
+                      <el-input v-model="noUse.data" style="width: 100%"></el-input>
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="3" :offset="1">
+                    <el-button type="success">获取验证码</el-button>
+                  </el-col>
+                </el-col>
+              </el-form>
+            </el-col>
+            <el-col :span="12" v-if="dialogTitle === '新增HR'">
+              qrcode
+            </el-col>
+          </el-row>
+
+          <div slot="footer" class="dialog-footer">
+            <el-button @click="dialog = false">取 消</el-button>
+            <el-button type="primary" @click="onSubmit">确 定</el-button>
+          </div>
+        </el-dialog>
+        <bind :display="bindDialog" :mobile="mobile" @close="closeAlert">
+          <el-col :span="24" style="text-align:center;font-size:1.25rem;padding-top:1rem;">
+            扫码更换微信!
+          </el-col>
+          <template v-slot:footer>
+            <el-col :span="24" style="text-align:center;">
+              <el-button @click="closeAlert('bind')">返回</el-button>
+            </el-col>
+          </template>
+        </bind>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import bind from '@/components/bind.vue';
+// import search from '@/components/search.vue';
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  metaInfo: {
+    title: 'hr管理',
+  },
+  components: {
+    listNormal,
+    bind,
+    // search,
+  },
+  data: () => ({
+    dataList: [],
+    input: '',
+    currentPage: 1,
+    totalRow: 1,
+    dialogTitle: '',
+    dialogWidth: 12,
+    dialog: false,
+    form: {},
+    noUse: {},
+    pwInput: '',
+    mobile: '',
+    bindDialog: false,
+  }),
+  created() {
+    this.search();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['getList', 'hrOperation']),
+    async search() {
+      let result;
+      if (this.input.length > 0) {
+        result = await this.hrOperation({ type: 'search', data: { tel: this.input } });
+        this.$set(this, `dataList`, [result.data]);
+      } else {
+        result = await this.hrOperation({ type: 'list', data: this.user.corpid });
+        this.$set(this, `dataList`, result.data);
+      }
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.search();
+    },
+    openAlert(type, data) {
+      this.$set(this, `dialogTitle`, `${type === 'add' ? '新增' : '修改'}HR`);
+      this.$set(this, `dialogWidth`, type === 'add' ? 12 : 24);
+      this.$set(this, `dialog`, true);
+      if (type !== 'add') {
+        this.$set(this, `form`, JSON.parse(JSON.stringify(data)));
+      }
+    },
+    closeAlert(type) {
+      if (type === 'bind') {
+        this.bindDialog = false;
+        this.$set(this, `mobile`, '');
+        return;
+      }
+      this.dialog = false;
+      this.form = {};
+      this.pwInput = '';
+    },
+    async onSubmit() {
+      let type; //? 'update' : 'add';
+      let data = JSON.parse(JSON.stringify(this.form));
+      if (this.form._id) {
+        type = 'update';
+        let { _id, passwd } = data;
+        data = { id: _id, passwd: passwd };
+      } else {
+        type = 'add';
+      }
+      let result = await this.hrOperation({
+        type: type,
+        data: { corpid: this.user.corpid, info: data },
+      });
+      if (`${result.errcode}` === `0`) {
+        this.$message.success(type === 'add' ? '添加成功' : '修改成功');
+        this.closeAlert();
+        this.search();
+      }
+    },
+    async toDelete(item) {
+      console.log(item);
+      this.$confirm('您确定注销此HR吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(async () => {
+          let result = await this.hrOperation({ type: 'delete', data: { id: item._id } });
+          console.log(result);
+          if (`${result.errcode}` === '0') {
+            this.$message.success('注销成功');
+            this.search();
+          }
+        })
+        .catch(() => {});
+    },
+    toBind(item) {
+      this.mobile = JSON.parse(JSON.stringify(item.mobile));
+      this.bindDialog = true;
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.icons {
+  zoom: 2;
+  position: relative;
+  top: 0.5rem;
+}
+</style>

+ 487 - 0
src/views/jobs/jobfair/detail.vue

@@ -0,0 +1,487 @@
+<template>
+  <div id="jobfair-detail">
+    <detail-layout>
+      <template #title>
+        <back-bar to="/jobs/jobfair/index" :title="`申请招聘会信息`"></back-bar>
+      </template>
+      <template #main>
+        <el-row class="Name">
+          <el-form class="form_content" ref="form" :model="info" label-width="7.5rem" label-position="left" :rules="rules">
+            <el-form-item label="招聘简章" prop="info">
+              <el-select v-model="info.profile_id" placeholder="请选择招聘简章" style="width:100%" @change="getProfile">
+                <el-option v-for="(item, index) in profileList" :key="index" :label="item.title" :value="item.id"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="申请说明" prop="apply_desc">
+              <el-input v-model="info.apply_desc" placeholder="请填写申请说明"></el-input>
+            </el-form-item>
+            <el-form-item label="" align="right">
+              <el-button icon="el-icon-zoom-in" type="success" @click="dialogSearchVisible = true">选择职位</el-button>
+              <el-button icon="el-icon-plus" type="success" @click="dialogAddVisible = true">新增职位</el-button>
+            </el-form-item>
+            <el-form-item label="招聘需求">
+              <!-- 最后需要将id拿出来整合进数据中: jobs字段:array -->
+              <el-table :data="jobList" border style="width: 100%" size="small">
+                <el-table-column prop="job_name" label="岗位名称" width="180" align="center"> </el-table-column>
+                <el-table-column prop="zy_req" label="需求专业" width="150" align="center"> </el-table-column>
+                <el-table-column prop="xl_req" label="学历要求" width="120" align="center"> </el-table-column>
+                <el-table-column prop="salary" label="薪资待遇" width="120" align="center"> </el-table-column>
+                <el-table-column prop="job_number" label="招聘人数" width="100" align="center"> </el-table-column>
+                <el-table-column fixed="right" label="操作" width="100">
+                  <template v-slot="scoped">
+                    <el-button type="text" @click="delJobs(scoped.$index, scoped.row)" size="small">删除</el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </el-form-item>
+            <el-form-item label="" align="right">
+              <el-button icon="el-icon-plus" type="success" @click="perDialog = true">新增人员</el-button>
+            </el-form-item>
+            <el-form-item label="参会人员">
+              <!-- 最后需要拿出来整合进数据中: attendee:array -->
+              <el-table :data="attend_list" border style="width: 100%">
+                <el-table-column prop="name" label="姓名" type="text" align="center"> </el-table-column>
+                <el-table-column prop="gender" label="性别" type="select" align="center"> </el-table-column>
+                <el-table-column prop="nation" label="民族" align="center"> </el-table-column>
+                <el-table-column prop="position" label="职务职称" align="center"> </el-table-column>
+                <el-table-column prop="mobile" label="手机号码" align="center"> </el-table-column>
+                <el-table-column prop="email" label="邮箱" align="center"> </el-table-column>
+
+                <el-table-column label="操作">
+                  <template v-slot="scoped">
+                    <el-button type="text" @click="delAttend(scoped.$index, scoped.row)" size="small">删除</el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </el-form-item>
+
+            <!-- <el-form-item label="招聘海报">
+              <el-upload class="upload-demo" drag action="https://jsonplaceholder.typicode.com/posts/" multiple>
+                <i class="el-icon-upload"></i>
+                <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+                <div class="el-upload__tip" slot="tip">最大可上传文件大小:100kb,支持上传 .doc、 .docx 文件</div>
+              </el-upload>
+            </el-form-item> -->
+            <el-row type="flex" justify="center">
+              <el-col :span="6">
+                <el-button type="success" style="width:60%" @click="toSubmit()">
+                  <span v-if="$route.query.id">保&nbsp;&nbsp;&nbsp;&nbsp;存</span>
+                  <span v-else>申&nbsp;&nbsp;&nbsp;&nbsp;请</span>
+                </el-button>
+              </el-col>
+            </el-row>
+          </el-form>
+
+          <el-dialog title="选择职位" :visible.sync="dialogSearchVisible">
+            <!-- <el-form-item>
+                <el-input placeholder="请输入职位名称" v-model="jobsForm.keyword" class="input-with-select">
+                  <el-select v-model="jobsForm.type" slot="prepend" placeholder="请选择">
+                    <el-option v-for="item in type_list" :key="item.type_value" :label="item.type_label" :value="item.type_value"> </el-option>
+                  </el-select>
+                  <el-button slot="append" icon="el-icon-search"></el-button>
+                </el-input>
+              </el-form-item> -->
+            <el-table :data="jobChooseList" border style="width: 100%" max-height="300">
+              <el-table-column type="index" label="序号" width="80" align="center"> </el-table-column>
+              <el-table-column prop="job_name" label="职位名称" width="400" align="left"> </el-table-column>
+              <el-table-column align="center" prop="is_practice" label="工作性质" sortable>
+                <template v-slot="scoped">
+                  {{ `${scoped.row.is_practice}` === `0` ? '全职' : '实习' }}
+                </template>
+              </el-table-column>
+              <el-table-column fixed="right" label="操作" width="100">
+                <template v-slot="scoped">
+                  <el-button type="text" @click="jobsSel(scoped.$index, scoped.row)" size="small">选择</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </el-dialog>
+
+          <el-dialog title="新增职位" width="70%" :visible.sync="dialogAddVisible">
+            <job :isDialog="true" @jobSubmit="jobSubmit"></job>
+          </el-dialog>
+
+          <el-dialog title="参会人员信息" :visible.sync="perDialog" center>
+            <el-form :model="perForm" label-position="left" label-width="auto" :rules="perRules" ref="preForm">
+              <el-form-item label="姓名" prop="name">
+                <el-input v-model="perForm.name" placeholder="请填写姓名,必填"></el-input>
+              </el-form-item>
+              <el-form-item label="性别" prop="gender">
+                <el-select v-model="perForm.gender" placeholder="请选择性别" style="width:100%">
+                  <el-option label="女" value="女"> </el-option>
+                  <el-option label="男" value="男"> </el-option>
+                </el-select>
+              </el-form-item>
+              <el-form-item label="民族" prop="nation">
+                <el-select v-model="perForm.nation" placeholder="请选择民族(可搜索)" filterable style="width:100%">
+                  <el-option v-for="item in nation_list" :key="item.nation_name" :label="item.nation_name" :value="item.nation_name"> </el-option>
+                </el-select>
+              </el-form-item>
+              <el-form-item label="职务职称" prop="position">
+                <el-input v-model="perForm.position" placeholder="请填写职务职称"></el-input>
+              </el-form-item>
+              <el-form-item label="手机号码" prop="mobile">
+                <el-input :maxlength="11" v-model="perForm.mobile" placeholder="请填写手机号码,必填"></el-input>
+              </el-form-item>
+              <el-form-item label="邮箱" prop="email">
+                <el-input v-model="perForm.email" placeholder="请填写邮箱"></el-input>
+              </el-form-item>
+            </el-form>
+            <template #footer>
+              <el-button @click="closeAttend()">取 消</el-button>
+              <el-button type="primary" @click="proAttend">确 定</el-button>
+            </template>
+          </el-dialog>
+        </el-row>
+      </template>
+    </detail-layout>
+  </div>
+</template>
+
+<script>
+import backBar from '@/components/back-bar.vue';
+import job from '@/views/manager/jobs/detail.vue';
+import detailLayout from '@/layout/detail-layout.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'talk-detail',
+  components: {
+    detailLayout,
+    job,
+    backBar,
+  },
+  data: () => ({
+    profileList: [],
+    jobChooseList: [],
+    jobList: [],
+    OrJobList: [],
+    info: {},
+    query: {},
+    perForm: {},
+    attend_list: [],
+    nation_list: [
+      { nation_name: '汉族' },
+      { nation_name: '回族' },
+      { nation_name: '蒙古族' },
+      { nation_name: '藏族' },
+      { nation_name: '维吾尔族' },
+      { nation_name: '苗族' },
+      { nation_name: '彝族' },
+      { nation_name: '壮族' },
+      { nation_name: '布依族' },
+      { nation_name: '朝鲜族' },
+      { nation_name: '满族' },
+      { nation_name: '侗族' },
+      { nation_name: '瑶族' },
+      { nation_name: '白族' },
+      { nation_name: '土家族' },
+      { nation_name: '哈尼族' },
+      { nation_name: '哈萨克族' },
+      { nation_name: '傣族' },
+      { nation_name: '黎族' },
+      { nation_name: '傈僳族' },
+      { nation_name: '佤族' },
+      { nation_name: '畲族' },
+      { nation_name: '高山族' },
+      { nation_name: '拉祜族' },
+      { nation_name: '水族' },
+      { nation_name: '东乡族' },
+      { nation_name: '纳西族' },
+      { nation_name: '景颇族' },
+      { nation_name: '柯尔克孜族' },
+      { nation_name: '土族' },
+      { nation_name: '达斡尔族' },
+      { nation_name: '仫佬族' },
+      { nation_name: '羌族' },
+      { nation_name: '布朗族' },
+      { nation_name: '撒拉族' },
+      { nation_name: '毛南族' },
+      { nation_name: '仡佬族' },
+      { nation_name: '锡伯族' },
+      { nation_name: '阿昌族' },
+      { nation_name: '普米族' },
+      { nation_name: '塔吉克族' },
+      { nation_name: '怒族' },
+      { nation_name: '乌孜别克族' },
+      { nation_name: '俄罗斯族' },
+      { nation_name: '鄂温克族' },
+      { nation_name: '德昂族' },
+      { nation_name: '保安族' },
+      { nation_name: '裕固族' },
+      { nation_name: '京族' },
+      { nation_name: '塔塔尔族' },
+      { nation_name: '独龙族' },
+      { nation_name: '鄂伦春族' },
+      { nation_name: '赫哲族' },
+      { nation_name: '门巴族' },
+      { nation_name: '珞巴族' },
+      { nation_name: '基诺族' },
+      { nation_name: '其他' },
+    ],
+    perDialog: false,
+    dialogSearchVisible: false,
+    dialogAddVisible: false,
+    jobsForm: {}, //查询职位,先留着
+    rules: {
+      apply_desc: [{ required: true, message: '请填写申请说明', trigger: 'change' }],
+      profile_id: [{ required: true, message: '请选择招聘简章', trigger: 'change' }],
+    },
+    perRules: {
+      name: [{ required: true, message: '请填写姓名', trigger: 'blur' }],
+      mobile: [{ required: true, message: '请填写手机号', trigger: 'blur' }],
+    },
+  }),
+  created() {
+    if (this.$route.query.id) {
+      this.search();
+    } else {
+      this.searchJobfair();
+    }
+    this.otherList();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['profilesOperation', 'postOperation', 'jobfairOperation']),
+    async search() {
+      let result = await this.jobfairOperation({ type: 'searchApply', data: { id: this.$route.query.id } });
+      this.$set(this, `info`, result.data);
+      this.$set(this, `attend_list`, result.data.attendee);
+      this.$set(this, `jobList`, result.data.jobs);
+    },
+    async submit() {
+      let jobs = this.jobList.map(item => item.id);
+      let attendee = this.attend_list.map(item => item);
+      let info = JSON.parse(JSON.stringify(this.info));
+      info.jobs = jobs;
+      info.attendee = attendee;
+      let result;
+      if (info.id) {
+        result = await this.jobfairOperation({ type: 'applyUpdate', data: { id: this.$route.query.id, info: info } });
+      } else {
+        result = await this.jobfairOperation({
+          type: 'apply',
+          data: { ...this.query, info: info },
+        });
+      }
+      if (`${result.errcode}` === '0') {
+        this.$message.success('操作成功');
+        this.$router.push({ path: '/jobs/jobfair/index' });
+      } else {
+        this.$message.error(result.errmsg || 'error');
+      }
+    },
+    async searchJobfair() {
+      let result = await this.jobfairOperation({ type: 'search', data: { id: this.$route.query.fair_id } });
+      if (`${result.errcode}` === '0') {
+        let { schid, schname, corpid, corpname } = result.data;
+        let query = { schid: schid, schname: schname, fairid: this.$route.query.fair_id, corpid: this.user.corpid, corpname: this.user.corpname };
+        this.$set(this, `query`, query);
+      }
+    },
+    async getProfile(select) {
+      let result = await this.profilesOperation({ type: 'search', data: { id: select } });
+      if (`${result.errcode}` === `0`) {
+        this.$set(this.info, `content`, result.data.content);
+      }
+    },
+    // 选择职位
+    jobsSel(index, val) {
+      let res = this.jobList.filter(item => item.id === val.id);
+      if (res.length <= 0) {
+        this.jobList.push(val);
+      }
+      this.resetJobList();
+      this.dialogSearchVisible = false;
+    },
+    // 删除职位
+    delJobs(index) {
+      this.jobList.splice(index, 1);
+      this.resetJobList();
+    },
+    toAttend() {
+      this.perDialog = true;
+    },
+    // 删除参会人员
+    delAttend(index, val) {
+      this.attend_list.splice(index, 1);
+    },
+    //处理参会人员
+    proAttend() {
+      this.$refs['preForm'].validate(valid => {
+        if (valid) {
+          this.attend_list.push(this.perForm);
+          this.perForm = {};
+          this.perDialog = false;
+        } else {
+          console.log('error submit!!');
+          return false;
+        }
+      });
+    },
+    closeAttend() {
+      this.perForm = {};
+      this.perDialog = false;
+    },
+    jobSubmit(value) {
+      if (value) {
+        this.$set(this, `dialogAddVisible`, false);
+        this.otherList();
+      }
+    },
+    toSubmit() {
+      this.$refs['form'].validate(valid => {
+        if (valid) {
+          this.submit();
+        } else {
+          console.log('error submit!!');
+          return false;
+        }
+      });
+    },
+    async otherList() {
+      let result = await this.postOperation({ type: 'list', data: { corpid: this.user.corpid } });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result。data的值
+        this.$set(this, `jobChooseList`, result.data);
+        this.$set(this, `OrJobList`, result.data);
+        this.resetJobList();
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+      result = await this.profilesOperation({ type: 'list', data: { corpid: this.user.corpid } });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result。data的值
+        this.$set(this, `profileList`, result.data);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    //重置职位列表
+    async resetJobList() {
+      let data = JSON.parse(JSON.stringify(this.OrJobList));
+      let selectList = [];
+      let chooseList = [];
+      if (this.jobList.length > 0) {
+        this.jobList.map(item => {
+          let val = typeof item === 'object' ? item.id : item;
+          let selected = _.find(data, select => select.id === val);
+          selectList.push(selected);
+        });
+        // chooseList = _.differenceBy(data, selectList, 'id');
+        chooseList = _.differenceWith(data, selectList, (item, value) => {
+          return item.id === value.id;
+        });
+      } else {
+        chooseList = data;
+      }
+      this.$set(this, `jobList`, selectList);
+      this.$set(this, `jobChooseList`, chooseList);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.el-dialog__body {
+  padding: 0;
+}
+li {
+  list-style: none;
+}
+p {
+  margin: 0;
+  padding: 0;
+}
+.form_content {
+  position: relative;
+  padding: 30px 20px;
+  background-color: #f4f5f9;
+  border: 1px solid #e7e8ec;
+}
+.tit h4 {
+  color: #333;
+  font-size: 15px;
+}
+.search {
+  width: 100%;
+  float: left;
+  height: 33px;
+  margin-bottom: 10px;
+  color: #333;
+  font-size: 15px;
+  margin-bottom: 10px;
+}
+/deep/.searchInput .el-input__inner {
+  height: 33px;
+  line-height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+}
+/deep/.searchBtn .el-button {
+  width: 35px;
+  height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+  padding: 6px 7px;
+  color: #555555;
+  font-weight: bold;
+  font-size: 15px;
+  background-color: #eeeeee;
+}
+
+.talkli {
+  float: left;
+}
+.Name {
+  float: left;
+  width: 100%;
+}
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.CagegoryBox {
+  position: absolute;
+  width: 94%;
+  height: 340px;
+  padding: 20px;
+  overflow: auto;
+  background-color: #fff;
+  border: 1px solid #85bee5;
+  box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.3);
+  display: flex;
+  flex-direction: column;
+  z-index: 99999;
+}
+.CagegoryListBox li {
+  display: inline-block;
+  text-align: left;
+  width: 100px;
+  padding: 6px 3px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  cursor: pointer;
+}
+#majors {
+  margin-top: 25px;
+  border: 1px solid #ddd;
+}
+#majors .category {
+  min-width: 70px;
+  max-width: 180px;
+  padding: 0 10px;
+  background-color: #eee;
+  float: left;
+}
+#majors .list {
+  padding: 10px 20px 0;
+  overflow: auto;
+  height: 100%;
+  width: 70%;
+  float: left;
+}
+</style>

+ 164 - 0
src/views/jobs/jobfair/index.vue

@@ -0,0 +1,164 @@
+<template>
+  <div id="index">
+    <list-tab :tab="tabsTitle">
+      <template v-slot:title>
+        双选会
+      </template>
+      <template v-slot:main1>
+        <list :dataList="firstList" :totalRow="firstTotalRow" @changePage="search" type="first"></list>
+      </template>
+      <template v-slot:main2>
+        <list :dataList="myList" :totalRow="myTotalRow" @changePage="search" type="my"></list>
+      </template>
+    </list-tab>
+  </div>
+</template>
+
+<script>
+import list from './list.vue';
+import listTab from '@/layout/list-tab.vue';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  metaInfo: {
+    title: '招聘会管理',
+  },
+  components: {
+    list,
+    listTab,
+  },
+  data: () => ({
+    firstTotalRow: 60,
+    myTotalRow: 30,
+    tabsTitle: ['双选会', '我的报名'],
+    firstList: [],
+    myList: [],
+  }),
+  created() {
+    this.search({ type: 'first' });
+    this.search({ type: 'my' });
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['jobfairOperation']),
+    // changePage({ type, currentPage }) {
+    //   //type=>哪个标签页
+    //   //currentPage=>标签页的当前页,用来计算skip的
+    //   if (type === 'myList') this.mySearch(currentPage);
+    //   else this.search(currentPage);
+    //   //根据type查询对应的数据
+    // },
+    async search({ type, currentPage }) {
+      let skip = 0;
+      if (currentPage) {
+        skip = (currentPage - 1) * this.$limit;
+      }
+      let newType = type === 'my' ? 'myList' : 'list';
+      let result = await this.jobfairOperation({
+        type: newType,
+        data: { corpid: type === 'my' ? this.user.corpid : undefined, skip: skip, limit: this.$limit },
+      });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result。data的值
+        this.$set(this, `${type}TotalRow`, result.total);
+        let newList;
+        if (type === 'my') {
+          newList = await this.proMyList(result.data);
+        } else {
+          newList = result.data;
+        }
+        this.$set(this, `${type}List`, newList);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    async proMyList(list) {
+      let arr = [];
+      for (const item of list) {
+        let object = await this.searchJobfair(item);
+        arr.push(object);
+      }
+      return arr;
+    },
+    async searchJobfair(object) {
+      let result = await this.jobfairOperation({ type: 'search', data: { id: object.fairid } });
+      if (`${result.errcode}` === '0') {
+        //需要myList显示什么东西,少的自己来这加
+        let { title, organizer, city, schname, time } = result.data;
+        object = { ...object, title, organizer, city, schname, time };
+      }
+      return object;
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+li {
+  list-style: none;
+}
+p {
+  margin: 0;
+  padding: 0;
+}
+.tit h4 {
+  color: #333;
+  font-size: 15px;
+}
+.search {
+  width: 100%;
+  float: left;
+  height: 33px;
+  margin-bottom: 10px;
+  color: #333;
+  font-size: 15px;
+  margin-bottom: 10px;
+}
+/deep/.searchInput .el-input__inner {
+  height: 33px;
+  line-height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+}
+/deep/.searchBtn .el-button {
+  width: 35px;
+  height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+  padding: 6px 7px;
+  color: #555555;
+  font-weight: bold;
+  font-size: 15px;
+  background-color: #eeeeee;
+}
+.add {
+  float: right;
+  width: 69px;
+  height: 34px;
+}
+.add .el-button {
+  float: right;
+  padding: 0;
+  width: 69px;
+  height: 34p;
+  height: 34px;
+  text-align: center;
+  background: #44b549;
+  color: #fff;
+  font-size: 14px;
+  font-weight: bold;
+}
+.talkli {
+  float: left;
+}
+.Name {
+  float: left;
+  width: 100%;
+}
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+</style>

+ 103 - 0
src/views/jobs/jobfair/info.vue

@@ -0,0 +1,103 @@
+<template>
+  <div id="info">
+    <detail-layout>
+      <template #title>
+        <back-bar to="/jobs/jobfair/index" :title="`双选会`"></back-bar>
+      </template>
+      <template #main>
+        <el-row class="titles">
+          <el-col :span="24">招聘会信息</el-col>
+        </el-row>
+        <el-row type="flex" align="middle" justify="center">
+          <el-col :span="20">
+            <el-row class="top__info">
+              <el-col :span="24">{{ info.title }}</el-col>
+              <el-col :span="24">
+                时间:
+                <span>{{ info.time }}</span>
+              </el-col>
+              <el-col :span="24">
+                主办方:
+                <span>{{ info.organizer }}</span>
+              </el-col>
+              <el-col :span="24">
+                地点:
+                <span>{{ info.address }}</span>
+              </el-col>
+            </el-row>
+          </el-col>
+          <el-col :span="4"></el-col>
+        </el-row>
+
+        <el-row class="titles">
+          <el-col :span="24">招聘会说明</el-col>
+        </el-row>
+        <el-row style="padding:1rem;">
+          {{ info.content }}
+        </el-row>
+
+        <el-row class="titles">
+          <el-col :span="24">注意事项</el-col>
+        </el-row>
+        <el-row style="padding:1rem;">
+          {{ info.notice }}
+        </el-row>
+      </template>
+    </detail-layout>
+  </div>
+</template>
+
+<script>
+import backBar from '@/components/back-bar.vue';
+import detailLayout from '@/layout/detail-layout.vue';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'info',
+  props: {},
+  components: {
+    backBar,
+    detailLayout,
+  },
+  data: () => ({
+    info: {},
+  }),
+  created() {
+    if (this.$route.query.id) {
+      this.search();
+    }
+  },
+  computed: {},
+  methods: {
+    ...mapActions(['jobfairOperation']),
+    async search() {
+      let result = await this.jobfairOperation({ type: 'search', data: { id: this.$route.query.id } });
+      console.log(result);
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `info`, result.data);
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.titles {
+  border-left: 0.1875rem solid #409eff;
+  padding-left: 1rem;
+  font-size: 1rem;
+  margin-top: 1rem;
+}
+.top__info {
+  .el-col {
+    padding: 0.5rem 0 0 1rem;
+    color: #666666;
+    font-size: 0.875rem;
+    span {
+      padding-left: 0.2rem;
+    }
+  }
+  .el-col:first-child {
+    color: #333333;
+  }
+}
+</style>

+ 127 - 0
src/views/jobs/jobfair/list.vue

@@ -0,0 +1,127 @@
+<template>
+  <div id="jobfairlist">
+    <!-- <el-row :span="24" :gutter="10" class="search">
+      <el-col :span="5" class="searchInput">
+        <el-input placeholder="请输入主办方" v-model="keyword" class="input-with-select"> </el-input>
+      </el-col>
+      <el-col :span="2" style="padding-bottom: 0.5rem;">
+        <el-button type="info" circle icon="el-icon-search icons"></el-button>
+      </el-col>
+    </el-row> -->
+    <el-table :data="list" border style="width: 100%">
+      <el-table-column fixed type="index" label="序号" width="80" align="center"> </el-table-column>
+      <el-table-column prop="title" label="双选会名称">
+        <template v-slot="scoped">
+          <el-row>
+            <el-col :span="24">
+              <el-link type="primary" @click="$router.push({ path: '/jobs/jobfair/info', query: { id: scoped.row.id } })">{{ scoped.row.title }}</el-link>
+            </el-col>
+          </el-row>
+        </template>
+      </el-table-column>
+      <el-table-column prop="organizer" label="主办方" width="150"></el-table-column>
+      <el-table-column prop="city" label="城市" width="120"></el-table-column>
+      <el-table-column prop="schname" label="学校" width="150" sortable></el-table-column>
+      <el-table-column prop="time" label="时间" width="150" sortable></el-table-column>
+      <el-table-column prop="status" label="状态" v-if="type === 'my'" sortable>
+        <template v-slot="scoped">
+          {{ `${scoped.row.status}` === '0' ? '未审核' : `${scoped.row.status}` === '1' ? '审核成功' : '审核失败' }}
+        </template>
+      </el-table-column>
+      <el-table-column fixed="right" label="操作">
+        <template v-slot="scoped">
+          <span v-if="type === 'my'">
+            <el-row>
+              <el-col :span="24">
+                <el-button
+                  v-if="`${scoped.row.status}` !== '1'"
+                  type="text"
+                  size="small"
+                  @click="$router.push({ path: '/jobs/jobfair/detail', query: { id: scoped.row.id } })"
+                  >修改信息
+                </el-button>
+                <el-button v-else type="text" size="small" :disabled="true">已参加</el-button>
+              </el-col>
+              <el-col :span="24">
+                <el-button v-if="`${scoped.row.status}` !== '1'" type="text" size="small" @click="toFeedback()">上传回执</el-button>
+              </el-col>
+            </el-row>
+          </span>
+          <span v-else>
+            <el-button v-if="scoped.row.type === '2'" type="text" size="small" :disabled="true">专场双选会/组团</el-button>
+            <el-button v-if="scoped.row.type === '1'" type="text" size="small" :disabled="true">不支持企业申请参加</el-button>
+            <el-button v-else @click="$router.push({ path: '/jobs/jobfair/detail', query: { fair_id: scoped.row.id } })" type="text" size="small">
+              申请入驻
+            </el-button>
+          </span>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-row class="row_pagination">
+      <el-col :span="24">
+        <el-pagination
+          @current-change="handleCurrentChange"
+          :current-page="currentPage"
+          :page-size="15"
+          layout="total, prev, pager, next, jumper"
+          :total="totalRow"
+        >
+        </el-pagination>
+      </el-col>
+    </el-row>
+    <el-dialog title="上传回执" :visible.sync="dialog" width="30%">
+      上传回执,还没调好接口
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'jobfairlist',
+  props: {
+    type: { type: String, defalut: 'none' },
+    dataList: { type: Array, defalut: () => [] },
+    totalRow: { type: Number, defalut: 20 },
+  },
+  components: {},
+  data: () => ({
+    keyword: '',
+    currentPage: 1,
+    list: [],
+    dialog: false,
+  }),
+  created() {},
+  watch: {
+    dataList: {
+      handler(val) {
+        this.$set(this, `list`, val);
+      },
+    },
+  },
+  computed: {},
+  methods: {
+    selectInfo(selection) {
+      //选择/全选方法,删除或者其他批量操作使用
+      let ids = [];
+      selection.map(item => {
+        ids.push(item.id);
+      });
+      this.$emit('selectInfo', { type: this.type, ids: ids });
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.$emit('changePage', { type: this.type, currentPage: this.currentPage });
+    },
+    toFeedback() {
+      this.dialog = true;
+    },
+  },
+};
+</script>
+
+<style lang="less" scopedd>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+</style>

+ 291 - 0
src/views/jobs/jobinfo/detail.vue

@@ -0,0 +1,291 @@
+<template>
+  <div id="detail">
+    <detail-layout>
+      <template #title>
+        <back-bar to="/jobs/jobinfo/index" title="在线招聘"></back-bar>
+      </template>
+      <template #main>
+        <el-row>
+          <el-form :model="info" :rules="rules" ref="form" class="form_content" label-width="7.5rem" label-position="left">
+            <el-form-item label="发布学校">
+              <el-select v-model="query.schid" placeholder="请选择学校" style="width:100%" @change="selectSch">
+                <el-option v-for="item in schList" :key="item.schid" :label="item.schname" :value="item.schid"> </el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="招聘简章" prop="profile_id">
+              <el-col :span="24">
+                <el-select v-model="info.profile_id" placeholder="请选择招聘简章" style="width:100%" @change="getProfile">
+                  <el-option v-for="(item, index) in profileList" :key="index" :label="item.title" :value="item.id"></el-option>
+                </el-select>
+              </el-col>
+            </el-form-item>
+            <!-- 单选框需要加form,添加验证 -->
+            <el-form-item label="招聘类型" prop="is_practice">
+              <el-radio-group v-model="info.is_practice">
+                <el-radio v-for="(item, index) in type_list" :key="index" :label="item.type_value" border>{{ item.type_label }}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item label="" align="right">
+              <el-button icon="el-icon-zoom-in" type="success" @click="dialogSearchVisible = true">选择职位</el-button>
+              <el-button icon="el-icon-plus" type="success" @click="dialogAddVisible = true">新增职位</el-button>
+            </el-form-item>
+            <el-form-item label="招聘需求">
+              <!-- 最后需要将id拿出来整合进数据中: jobs字段:array -->
+              <el-table :data="jobList" border style="width: 100%" size="small">
+                <el-table-column prop="job_name" label="岗位名称" width="180" align="center"> </el-table-column>
+                <el-table-column prop="zy_req" label="需求专业" width="150" align="center"> </el-table-column>
+                <el-table-column prop="xl_req" label="学历要求" width="120" align="center"> </el-table-column>
+                <el-table-column prop="salary" label="薪资待遇" width="120" align="center"> </el-table-column>
+                <el-table-column prop="job_number" label="招聘人数" width="100" align="center"> </el-table-column>
+                <el-table-column fixed="right" label="操作" width="100">
+                  <template v-slot="scoped">
+                    <el-button type="text" @click="delJobs(scoped.$index, scoped.row)" size="small">删除</el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </el-form-item>
+          </el-form>
+          <el-row type="flex" justify="center" class="detailBtn">
+            <el-col :span="4">
+              <el-button style="width:60%" @click="toSubmit(0)">保存为草稿</el-button>
+            </el-col>
+            <el-col :span="4">
+              <el-button type="success" style="width:60%" @click="toSubmit(1)">提交</el-button>
+            </el-col>
+          </el-row>
+        </el-row>
+      </template>
+    </detail-layout>
+    <el-dialog title="新增职位" width="70%" :visible.sync="dialogAddVisible">
+      <job :isDialog="true" @jobSubmit="jobSubmit"></job>
+    </el-dialog>
+    <el-dialog title="选择职位" :visible.sync="dialogSearchVisible">
+      <!-- <el-form-item>
+                <el-input placeholder="请输入职位名称" v-model="jobsForm.keyword" class="input-with-select">
+                  <el-select v-model="jobsForm.type" slot="prepend" placeholder="请选择">
+                    <el-option v-for="item in type_list" :key="item.type_value" :label="item.type_label" :value="item.type_value"> </el-option>
+                  </el-select>
+                  <el-button slot="append" icon="el-icon-search"></el-button>
+                </el-input>
+              </el-form-item> -->
+      <el-table :data="jobChooseList" border style="width: 100%" max-height="300">
+        <el-table-column type="index" label="序号" width="80" align="center"> </el-table-column>
+        <el-table-column prop="job_name" label="职位名称" align="left"> </el-table-column>
+        <el-table-column align="center" prop="is_practice" label="工作性质" sortable>
+          <template v-slot="scoped">
+            {{ `${scoped.row.is_practice}` === `0` ? '全职' : '实习' }}
+          </template>
+        </el-table-column>
+        <el-table-column fixed="right" label="操作" width="100">
+          <template v-slot="scoped">
+            <el-button type="text" @click="jobsSel(scoped.$index, scoped.row)" size="small">选择</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import backBar from '@/components/back-bar.vue';
+import job from '@/views/manager/jobs/detail.vue';
+import detailLayout from '@/layout/detail-layout.vue';
+import formItem from '@/components/form-item.vue';
+import { mapActions, mapState } from 'vuex';
+import _ from 'lodash';
+export default {
+  name: 'detail',
+  props: {},
+  components: {
+    // formItem,
+    job,
+    detailLayout,
+    backBar,
+  },
+  data: () => ({
+    info: {},
+    query: {},
+    OrJobList: [],
+    jobList: [],
+    jobChooseList: [],
+    schList: [],
+    loading: true,
+    dialogAdd: false,
+    dialogSearchVisible: false,
+    dialogAddVisible: false,
+    profileList: [],
+    radio: '1',
+    type_list: [{ type_value: 0, type_label: '全职' }, { type_value: 1, type_label: '实习' }],
+    rules: {
+      profile_id: [{ required: true, message: '请输入内容', trigger: 'change' }],
+      is_practice: [{ required: true, message: '请选择招聘类型', trigger: 'change' }],
+    },
+  }),
+  created() {
+    this.search();
+    this.otherList();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['profilesOperation', 'jobinfoOperation', 'postOperation', 'corpSchInfo']),
+    async search() {
+      if (this.$route.query.id) {
+        this.$set(this, `loading`, false);
+        let result = await this.jobinfoOperation({ type: 'search', data: { id: this.$route.query.id } });
+        this.$set(this, `info`, result.data);
+        this.$set(this.query, `schid`, this.info.schid);
+        this.$set(this.query, `schname`, this.info.schname);
+        this.$set(this, `jobList`, result.data.jobs);
+        this.$set(this, `loading`, true);
+      }
+    },
+    toSubmit(state) {
+      this.info.is_submit = state;
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          this.submit();
+        }
+        return false;
+      });
+    },
+    async submit() {
+      let result;
+      let jobs = this.jobList.map(item => item.id);
+      let info = JSON.parse(JSON.stringify(this.info));
+      info.jobs = jobs;
+      if (info.id) {
+        result = await this.jobinfoOperation({ type: 'update', data: { info: info, id: this.$route.query.id } });
+      } else {
+        this.query = { ...this.query, corpid: this.user.corpid, corpname: this.user.corpname };
+        result = await this.jobinfoOperation({
+          type: 'add',
+          data: { info: info, ...this.query },
+        });
+      }
+      if (`${result.errcode}` === '0') {
+        this.$message.success('操作成功');
+        this.$router.push('/jobs/jobinfo/index');
+      } else {
+        this.$message.error(result.errmsg);
+      }
+    },
+    jobSubmit(value) {
+      if (value) {
+        this.$set(this, `dialogAdd`, false);
+        this.otherList();
+      }
+    },
+    selectSch(select) {
+      let res = this.schList.filter(item => {
+        return `${item.schid}` === `${select}`;
+      });
+      if (res.length > 0) {
+        this.$set(this.query, `schname`, res[0].schname);
+      }
+    },
+    // 选择职位
+    jobsSel(index, val) {
+      let res = this.jobList.filter(item => item.id === val.id);
+      if (res.length <= 0) {
+        this.jobList.push(val);
+      }
+      this.resetJobList();
+      this.dialogSearchVisible = false;
+    },
+    // 删除职位
+    delJobs(index) {
+      this.jobList.splice(index, 1);
+      this.resetJobList();
+    },
+    async getProfile(select) {
+      let result = await this.profilesOperation({ type: 'search', data: { id: select } });
+      if (`${result.errcode}` === `0`) {
+        this.$set(this.info, `content`, result.data.content);
+        this.$set(this.info, `title`, result.data.title);
+      }
+    },
+    async otherList() {
+      let result = await this.postOperation({ type: 'list', data: { corpid: this.user.corpid } });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result。data的值
+        this.$set(this, `jobChooseList`, result.data);
+        this.$set(this, `OrJobList`, result.data);
+        this.resetJobList();
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+      result = await this.profilesOperation({ type: 'list', data: { corpid: this.user.corpid } });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result。data的值
+        this.$set(this, `profileList`, result.data);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+      result = await await this.corpSchInfo({ type: 'list', data: { corpid: this.user.corpid } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `schList`, result.data);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    //重置职位列表
+    async resetJobList() {
+      let data = JSON.parse(JSON.stringify(this.OrJobList));
+      let selectList = [];
+      let chooseList = [];
+      if (this.jobList.length > 0) {
+        this.jobList.map(item => {
+          let val = typeof item === 'object' ? item.id : item;
+          let selected = _.find(data, select => select.id === val);
+          selectList.push(selected);
+        });
+        // chooseList = _.differenceBy(data, selectList, 'id');
+        chooseList = _.differenceWith(data, selectList, (item, value) => {
+          return item.id === value.id;
+        });
+      } else {
+        chooseList = data;
+      }
+      this.$set(this, `jobList`, selectList);
+      this.$set(this, `jobChooseList`, chooseList);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.form_content {
+  position: relative;
+  padding: 30px 20px;
+  background-color: #f4f5f9;
+  border: 1px solid #e7e8ec;
+}
+.demand {
+  float: left;
+  width: 100%;
+  padding: 0px 50px;
+}
+.demandTitBtn {
+  float: right;
+  width: 100%;
+  text-align: right;
+}
+.demandTitBtn .el-button {
+  padding: 9px 12px;
+}
+.profileLi {
+  float: left;
+  width: 100%;
+  margin: 10px 0 0 0;
+}
+.detailBtn {
+  float: left;
+  width: 100%;
+  margin: 20px 0px;
+}
+.detailBtn .el-button {
+  padding: 12px 10px;
+}
+</style>

+ 217 - 0
src/views/jobs/jobinfo/index.vue

@@ -0,0 +1,217 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template v-slot:title>
+        在线招聘
+      </template>
+      <template #functionBar>
+        <el-col :span="5" class="searchInput">
+          <el-select v-model="searchInfo.is_practice" placeholder="选择招聘类型">
+            <el-option label="全部类型" :value="undefined"></el-option>
+            <el-option label="全职" :value="0"></el-option>
+            <el-option label="实习" :value="1"></el-option>
+          </el-select>
+        </el-col>
+        <el-col :span="5" class="searchInput">
+          <el-select v-model="searchInfo.status" placeholder="选择审核状态">
+            <el-option label="全部类型" :value="undefined"></el-option>
+            <el-option label="未审核" value="0"></el-option>
+            <el-option label="审核通过" value="1"></el-option>
+            <el-option label="审核失败" value="2"></el-option>
+          </el-select>
+        </el-col>
+        <el-col :span="2">
+          <el-button type="info" circle icon="el-icon-search icons" @click="search()"></el-button>
+        </el-col>
+      </template>
+      <template #addBtn>
+        <el-col>
+          <el-button type="success" icon="el-icon-plus" @click="$router.push('/jobs/jobinfo/detail')">新增</el-button>
+        </el-col>
+      </template>
+      <template #main>
+        <el-row class="profileLi">
+          <el-table :data="PostInfos" style="width: 100%" border>
+            <el-table-column align="center" label="序号" type="index" width="50"> </el-table-column>
+            <el-table-column align="center" label="招聘标题" prop="title" width="180"> </el-table-column>
+            <el-table-column align="center" label="招聘类型" prop="is_practice" width="180" sortable>
+              <template v-slot="scoped">
+                {{ `${scoped.row.is_practice}` === `0` ? '全职招聘' : '实习招聘' }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="status" align="center" label="状态" sortable>
+              <template v-slot="scoped">
+                {{ `${scoped.row.status}` === '0' ? '未审核' : `${scoped.row.status}` === '1' ? '审核通过' : '审核失败' }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="is_submit" align="center" label="类型" sortable>
+              <template v-slot="scoped">
+                {{ `${scoped.row.is_submit}` === '0' ? '草稿' : '已发布' }}
+              </template>
+            </el-table-column>
+            <el-table-column align="center" label="操作">
+              <template v-slot="scoped">
+                <el-button size="mini" @click="$router.push({ path: '/jobs/jobinfo/detail', query: { id: scoped.row.id } })">修改</el-button>
+                <el-button size="mini" type="danger" @click="handleDelete(scoped.$index, scoped.row)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-row>
+        <el-row class="row_pagination">
+          <el-col :span="24">
+            <el-pagination @current-change="search" :current-page="currentPage" :page-size="$limit" layout="total, prev, pager, next, jumper" :total="totalRow">
+            </el-pagination>
+          </el-col>
+        </el-row>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {
+    listNormal,
+  },
+  data: () => ({
+    searchInfo: {},
+    currentPage: 1,
+    totalRow: 60,
+    PostInfos: [],
+  }),
+  created() {
+    this.search();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['jobinfoOperation']),
+    async search(val) {
+      let skip = 0;
+      if (val) {
+        this.currentPage = val;
+        skip = (this.currentPage - 1) * this.$limit;
+      }
+      let result = await this.jobinfoOperation({
+        type: 'list',
+        data: { corpid: this.user.corpid, skip: skip, limit: this.$limit, ...this.searchInfo },
+      });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result。data的值
+        this.$set(this, 'PostInfos', result.data);
+        this.$set(this, `totalRow`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    async handleDelete(index, item) {
+      this.$confirm('是否删除该招聘简章?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.toDelete(item.id);
+        })
+        .catch(() => {});
+    },
+    async toDelete(id) {
+      let result = await this.jobinfoOperation({ type: 'delete', data: { id: id } });
+      if (`${result.errcode}` === '0') {
+        this.$message.success('删除成功');
+        this.search();
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : '删除失败');
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.distance30 {
+  margin-top: 1.9rem;
+}
+.title_space {
+  margin: 0.9rem 0;
+  font-size: 1rem;
+}
+.distance10 {
+  margin-top: 0.62rem;
+}
+.down30 {
+  margin-bottom: 1.9rem;
+}
+.top_down10 {
+  margin: 1rem 0;
+}
+li {
+  list-style: none;
+}
+p {
+  margin: 0;
+  padding: 0;
+}
+.tit h4 {
+  color: #333;
+  font-size: 16px;
+  font-weight: 500;
+}
+.search {
+  width: 100%;
+  float: left;
+  height: 33px;
+  margin-bottom: 10px;
+  color: #333;
+  font-size: 15px;
+  margin-bottom: 10px;
+}
+/deep/.searchInput .el-input__inner {
+  height: 33px;
+  line-height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+}
+/deep/.searchBtn .el-button {
+  width: 35px;
+  height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+  padding: 6px 7px;
+  color: #555555;
+  font-weight: bold;
+  font-size: 15px;
+  background-color: #eeeeee;
+}
+.add {
+  float: right;
+  width: 69px;
+  height: 34px;
+}
+.add .el-button {
+  float: right;
+  padding: 0;
+  width: 69px;
+  height: 34p;
+  height: 34px;
+  text-align: center;
+  background: #44b549;
+  color: #fff;
+  font-size: 14px;
+  font-weight: bold;
+}
+/deep/.el-button--mini {
+  border: none;
+  color: #337ab7;
+  padding: 0;
+}
+/deep/.el-button--danger {
+  background: none;
+}
+</style>

+ 118 - 0
src/views/jobs/profile/detail.vue

@@ -0,0 +1,118 @@
+<template>
+  <div id="pro-detail">
+    <detail-layout>
+      <template v-slot:title>
+        <back-bar to="/jobs/profile/index" title="招聘简章"></back-bar>
+      </template>
+      <template #main>
+        <el-row>
+          <el-form :model="info" :rules="rules" ref="form">
+            <form-item label="招聘简章标题" prop="title">
+              <el-input v-model="info.title"></el-input>
+            </form-item>
+            <form-item label="内容" prop="content">
+              <wang-editor v-if="loading" v-model="info.content"></wang-editor>
+            </form-item>
+          </el-form>
+          <el-row type="flex" justify="center">
+            <el-col :span="6">
+              <el-button type="success" style="width:60%" @click="toSubmit()">保&nbsp;&nbsp;&nbsp;&nbsp;存</el-button>
+            </el-col>
+          </el-row>
+        </el-row>
+      </template>
+    </detail-layout>
+  </div>
+</template>
+
+<script>
+import backBar from '@/components/back-bar.vue';
+import upload from '@/components/upload.vue';
+import formItem from '@/components/form-item.vue';
+import detailLayout from '@/layout/detail-layout.vue';
+import WangEditor from '@/components/wang-editor.vue';
+import { mapActions, mapState } from 'vuex';
+import _ from 'lodash';
+export default {
+  name: 'pro-detail',
+  props: {
+    original: { type: Object, default: () => {} },
+  },
+  components: {
+    detailLayout,
+    formItem,
+    backBar,
+    WangEditor,
+  },
+  data: () => ({
+    info: {},
+    loading: true,
+    rules: {
+      title: [{ required: true, message: '请输入招聘简章标题', trigger: 'blur' }],
+      content: [{ required: true, message: '请输入内容', trigger: 'blur' }],
+    },
+  }),
+  created() {
+    if (this.$route.query.id) {
+      this.$set(this, `loading`, false);
+      this.search();
+    }
+  },
+  mounted() {
+    this.$nextTick(() => {
+      // this.$set(this, `info`, this.original);
+    });
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['profilesOperation']),
+    async search() {
+      let result = await this.profilesOperation({ type: 'search', data: { id: this.$route.query.id } });
+      this.$set(this, `info`, result.data);
+      this.$set(this, `loading`, true);
+    },
+    async submit() {
+      if (this.info.content === undefined) {
+        this.$message.error('请填写内容');
+        return;
+      }
+      let result;
+      if (this.info.id) {
+        result = await this.profilesOperation({ type: 'update', data: { info: this.info, id: this.$route.query.id } });
+      } else {
+        result = await this.profilesOperation({ type: 'add', data: { info: this.info, corpid: this.user.corpid, corpname: '福瑞test' } });
+      }
+      if (`${result.errcode}` === '0') {
+        this.$message.success('操作成功');
+        this.$router.push('/jobs/profile/index');
+      } else {
+        this.$message.error(result.errmsg);
+      }
+    },
+    toSubmit() {
+      this.$refs['form'].validate(valid => {
+        if (valid) {
+          this.submit();
+        } else {
+          console.log('error submit!!');
+          return false;
+        }
+      });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.tip {
+  color: #999999;
+}
+.mb1 {
+  margin-bottom: 1rem;
+}
+.btn_center {
+  text-align: center;
+}
+</style>

+ 185 - 0
src/views/jobs/profile/index.vue

@@ -0,0 +1,185 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template #title>
+        <el-row :span="24" class="tit">
+          招聘简章
+        </el-row>
+      </template>
+      <template #functionBar>
+        &nbsp;
+        <!-- <el-col :span="5" class="searchInput">
+          <el-input v-model="input" placeholder="请输入招聘简章标题"></el-input>
+        </el-col>
+        <el-col :span="2">
+          <el-button type="info" circle icon="el-icon-search icons"></el-button>
+        </el-col> -->
+      </template>
+      <template #addBtn>
+        <el-col>
+          <el-button type="success" icon="el-icon-plus" @click="$router.push('/jobs/profile/detail')">新增</el-button>
+        </el-col>
+      </template>
+      <template #main>
+        <el-table :data="list" style="width: 100%" border>
+          <el-table-column align="center" label="序号" type="index" width="50"> </el-table-column>
+          <el-table-column align="center" label="标题" prop="title"> </el-table-column>
+          <el-table-column align="center" label="操作">
+            <template v-slot="scoped">
+              <el-button size="mini" @click="$router.push({ path: '/jobs/profile/detail', query: { id: scoped.row.id } })">修改</el-button>
+              <el-button size="mini" type="danger" @click="handleDelete(scoped.$index, scoped.row)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <el-row class="row_pagination">
+          <el-col :span="24">
+            <el-pagination @current-change="search" :current-page="currentPage" :page-size="$limit" layout="total, prev, pager, next, jumper" :total="totalRow">
+            </el-pagination>
+          </el-col>
+        </el-row>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import listNormal from '@/layout/list-normal.vue';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {
+    listNormal,
+  },
+  data: () => ({
+    input: '',
+    currentPage: 1,
+    totalRow: 0,
+    list: [],
+  }),
+  created() {
+    //1,初始化组件,查询数据
+    this.search();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+
+  methods: {
+    ...mapActions(['profilesOperation']),
+    async search(val) {
+      let skip = 0;
+      if (val) {
+        this.currentPage = val;
+        skip = (this.currentPage - 1) * this.$limit;
+      }
+      //1直接拿着参数发送请求
+      let result = await this.profilesOperation({ type: 'list', data: { corpid: this.user.corpid, skip: skip, limit: this.$limit } });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result。data的值
+        this.$set(this, `list`, result.data);
+        this.$set(this, `totalRow`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    async handleDelete(index, item) {
+      this.$confirm('是否删除该招聘简章?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.toDelete(item.id);
+        })
+        .catch(() => {});
+    },
+    async toDelete(id) {
+      let result = await this.profilesOperation({ type: 'delete', data: { id: id } });
+      if (`${result.errcode}` === '0') {
+        this.$message.success('删除成功');
+        this.search();
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : '删除失败');
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.distance30 {
+  margin-top: 1.9rem;
+}
+.title_space {
+  margin: 0.9rem 0;
+  font-size: 1rem;
+}
+.distance10 {
+  margin-top: 0.62rem;
+}
+.down30 {
+  margin-bottom: 1.9rem;
+}
+.top_down10 {
+  margin: 1rem 0;
+}
+li {
+  list-style: none;
+}
+p {
+  margin: 0;
+  padding: 0;
+}
+.tit h4 {
+  color: #333;
+  font-size: 15px;
+  font-weight: 500;
+}
+.search {
+  width: 100%;
+  float: left;
+  height: 33px;
+  margin-bottom: 10px;
+  color: #333;
+  font-size: 15px;
+  margin-bottom: 10px;
+}
+// .profileLi {
+//   float: left;
+// }
+/deep/.searchInput .el-input__inner {
+  height: 33px;
+  line-height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+}
+/deep/.searchBtn .el-button {
+  width: 35px;
+  height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+  padding: 6px 7px;
+  color: #555555;
+  font-weight: bold;
+  font-size: 15px;
+  background-color: #eeeeee;
+}
+.add {
+  float: right;
+  width: 69px;
+  height: 34px;
+}
+.add .el-button {
+  float: right;
+  padding: 0;
+  width: 69px;
+  height: 34p;
+  height: 34px;
+  text-align: center;
+  background: #44b549;
+  color: #fff;
+  font-size: 14px;
+  font-weight: bold;
+}
+</style>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 472 - 0
src/views/jobs/school/detail.vue


+ 259 - 0
src/views/jobs/school/index.vue

@@ -0,0 +1,259 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template #title>
+        <el-row :span="24" class="tit">
+          <h4>入驻学校</h4>
+        </el-row>
+      </template>
+      <template #functionBar>
+        <el-col :span="5" class="searchInput">
+          <el-input v-model="input" placeholder="请输入学校名称"></el-input>
+        </el-col>
+        <el-col :span="2">
+          <el-button type="info" circle icon="el-icon-search icons"></el-button>
+        </el-col>
+      </template>
+      <template #main>
+        <el-row class="Name">
+          <el-menu :default-active="activeIndex" mode="horizontal" @select="handleSelect">
+            <el-menu-item v-for="(tag, index) in tags" :key="index" :index="tag.id">
+              <p class="tab-text">{{ tag.name }}</p>
+              <p class="tab-num">{{ tag.number }}</p>
+            </el-menu-item>
+          </el-menu>
+        </el-row>
+        <el-row>
+          <el-col :span="12" v-for="(tag, index) in tagsCon" :key="index" :type="tag.type">
+            <ul class="nameCon">
+              <li>
+                <el-col :span="24" class="nameConImg">
+                  <img src="../../../assets/schoolLogo.png" />
+                </el-col>
+                <el-col :span="24" class="nameConTit">
+                  <el-link class="nameConTit-txt" @click="$router.push('/jobs/school/detail')">{{ tag.name }}</el-link>
+                  <p class="nameConTit-pla">{{ tag.place }}</p>
+                </el-col>
+                <el-col :span="24" class="nameConBtn">
+                  <el-button @click="join(tag.id)">入驻</el-button>
+                </el-col>
+              </li>
+            </ul>
+          </el-col>
+        </el-row>
+        <el-row class="row_pagination">
+          <el-col :span="24">
+            <el-pagination
+              @current-change="handleCurrentChange"
+              :current-page="currentPage"
+              :page-size="15"
+              layout="total, prev, pager, next, jumper"
+              :total="totalRow"
+            >
+            </el-pagination>
+          </el-col>
+        </el-row>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import listNormal from '@/layout/list-normal.vue';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {
+    listNormal,
+  },
+  data: () => ({
+    input: '',
+    currentPage: 1,
+    totalRow: 60,
+    activeIndex: '1',
+    tags: [
+      { id: '1', name: '全国', number: '1008278' },
+      { id: '2', name: '安徽', number: '7776' },
+      { id: '3', name: '北京', number: '2177' },
+      { id: '4', name: '福建', number: '14885' },
+      { id: '5', name: '甘肃', number: '25178' },
+      { id: '6', name: '广东', number: '86858' },
+      { id: '7', name: '广西', number: '20302' },
+      { id: '8', name: '贵州', number: '21007' },
+      { id: '9', name: '河北', number: '28707' },
+      { id: '10', name: '河南', number: '15090' },
+      { id: '11', name: '黑龙江', number: '15090' },
+      { id: '12', name: '湖北', number: '45702' },
+      { id: '13', name: '湖南', number: '7689' },
+      { id: '14', name: '贵州', number: '21007' },
+      { id: '15', name: '河北', number: '28707' },
+      { id: '16', name: '河南', number: '15090' },
+      { id: '17', name: '黑龙江', number: '15090' },
+      { id: '18', name: '湖北', number: '45702' },
+      { id: '19', name: '湖南', number: '7689' },
+    ],
+    tagsCon: [
+      { name: '南昌大学', place: '江西 南昌 应届共11990人' },
+      { name: '昆明理工大学', place: '云南 昆明 应届共11253人' },
+      { name: '华南农业大学', place: '广东 广州 应届共10469人' },
+    ],
+    schList: {},
+  }),
+  created() {
+    this.getInfo();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['corpSchInfo', 'joinSch']),
+    async getInfo() {
+      let result = await this.corpSchInfo({ type: 'list', data: { corpid: this.user.corpid } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `schList`, result.data);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    async join(id) {
+      let result = await this.joinSch({ schid: 999991, corpid: this.user.corpid });
+      if (`${result.errcode}` === '0') {
+        this.$message.success('申请成功,请等待校方审核');
+      } else {
+        this.$message.error('申请失败,请重新尝试');
+      }
+    },
+    handleSelect(val) {
+      console.log(val);
+      //请求大学列表
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.search();
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.el-menu--horizontal > .el-menu-item {
+  height: 45px;
+  margin-bottom: 1rem;
+}
+li {
+  list-style: none;
+}
+p {
+  margin: 0;
+  padding: 0;
+}
+.tit h4 {
+  color: #333;
+  font-size: 16px;
+  font-weight: 500;
+}
+.search {
+  width: 100%;
+  float: left;
+  height: 33px;
+  margin-bottom: 10px;
+  color: #333;
+  font-size: 15px;
+  margin-bottom: 10px;
+}
+/deep/.searchInput .el-input__inner {
+  height: 33px;
+  line-height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+}
+/deep/.searchBtn .el-button {
+  width: 35px;
+  height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+  padding: 6px 7px;
+  color: #555555;
+  font-weight: bold;
+  font-size: 15px;
+  background-color: #eeeeee;
+}
+.Name {
+  float: left;
+  width: 100%;
+}
+.Name .nameTit {
+  float: left;
+  width: 100%;
+  padding: 0;
+  border-bottom: 1px solid #e7e7eb;
+}
+.Name .nameTit li {
+  float: left;
+  padding: 8px 15px;
+  text-align: center;
+}
+.tab-text {
+  font-size: 15px;
+  text-align: center;
+  height: 22px;
+  line-height: 22px;
+}
+.tab-num {
+  font-size: 12px;
+  text-align: center;
+  height: 19px;
+  line-height: 19px;
+}
+.nameCon {
+  float: left;
+}
+.nameCon li {
+  float: left;
+  width: 432px;
+  padding: 20px 0;
+  margin: 0 29px 0 0;
+  border-bottom: 1px solid #cccccc;
+}
+.nameCon li .nameConImg {
+  float: left;
+  width: 64px;
+  height: 64px;
+}
+.nameCon li .nameConTit {
+  float: left;
+  padding: 0 0 0 10px;
+  width: 280px;
+  height: 64px;
+  line-height: 35px;
+}
+.nameCon li .nameConTit p {
+  margin: 0 0 5px 0;
+}
+.nameConTit-txt {
+  font-size: 16px;
+  color: #333;
+  height: 25px;
+  line-height: 25px;
+}
+.nameConTit-pla {
+  font-size: 14px;
+  color: #666;
+  height: 22px;
+  line-height: 22px;
+}
+.nameCon li .nameConBtn {
+  float: right;
+  width: 70px;
+  height: 64px;
+  padding: 10px 0 0 5px;
+}
+.nameCon li .nameConBtn .el-button {
+  padding: 10px 15px;
+  background: #5cb85c;
+  border-radius: 4px;
+  color: #fff;
+  text-align: center;
+}
+</style>

+ 428 - 0
src/views/jobs/talk/detail.vue

@@ -0,0 +1,428 @@
+<template>
+  <div id="talk-detail">
+    <detail-layout>
+      <template #title>
+        <back-bar to="/jobs/talk/index" title="宣讲会"></back-bar>
+      </template>
+      <template #main>
+        <el-form class="form_content" ref="form" :model="info" label-width="0" label-position="left">
+          <form-item label="宣讲学校">
+            <el-select v-model="query.schid" placeholder="请选择学校" style="width:72%" @change="selectSch">
+              <el-option v-for="item in schList" :key="item.schid" :label="item.schname" :value="item.schid"> </el-option>
+            </el-select>
+            没有学校可选?点击<el-link href="/jobs/school/index" type="success">入驻学校</el-link>
+          </form-item>
+          <form-item label="招聘简章">
+            <el-col :span="24">
+              <el-select v-model="info.profile_id" placeholder="请输入招聘简章" style="width:100%" @change="getProfile">
+                <el-option v-for="(item, index) in profileList" :key="index" :label="item.title" :value="item.id"></el-option>
+              </el-select>
+            </el-col>
+          </form-item>
+          <!-- bff处理时,删除 -->
+          <!-- <form-item label="招聘简章内容">
+            <el-input v-model="info.content" placeholder="招聘简章内容"></el-input>
+          </form-item> -->
+          <form-item label="宣讲会标题(选填)">
+            <el-col :span="24">
+              <el-input v-model="info.title" placeholder="宣讲会标题默认为公司名,如无特殊情况请勿改动"></el-input>
+            </el-col>
+          </form-item>
+          <el-row style="text-align:right;padding:0 50px;">
+            <el-button icon="el-icon-zoom-in" type="success" @click="dialogSearchVisible = true">选择职位</el-button>
+            <el-button icon="el-icon-plus" type="success" @click="dialogAddVisible = true">新增职位</el-button>
+          </el-row>
+          <form-item label="招聘需求">
+            <el-table :data="jobList" border style="width: 100%" size="small">
+              <el-table-column prop="job_name" label="岗位名称" align="center"> </el-table-column>
+              <el-table-column prop="zy_req" label="需求专业" align="center"> </el-table-column>
+              <el-table-column prop="xl_req" label="学历要求" align="center"> </el-table-column>
+              <el-table-column prop="salary" label="薪资待遇" align="center"> </el-table-column>
+              <el-table-column prop="job_number" label="招聘人数" align="center"> </el-table-column>
+              <el-table-column fixed="right" label="操作">
+                <template v-slot="scoped">
+                  <el-button type="text" @click="delJobs(scoped.$index, scoped.row)" size="small">删除</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </form-item>
+          <form-item label="预定时间">
+            <el-date-picker v-model="info.date" type="date" placeholder="选择日期" format="yyyy 年 MM 月 dd 日" value-format="yyyy-MM-dd"> </el-date-picker>
+            <el-select v-model="info.time" placeholder="请选择" format="yyyy-MM-dd HH:mm">
+              <el-option v-for="item in career_time_list" :key="item.career_time" :label="item.career_time" :value="item.career_time"> </el-option>
+            </el-select>
+          </form-item>
+          <form-item label="申请说明">
+            <el-input
+              v-model="info.apply_desc"
+              placeholder="宣讲会时间(请写明具体日期与时间段,另外添加2个时间做备选)、宣讲教室要求(教室大小,多媒体需求等)"
+            ></el-input>
+          </form-item>
+          <form-item label="来访人数">
+            <el-input v-model="info.team_size" placeholder="请填写来访人数"></el-input>
+          </form-item>
+          <form-item label="带队领导">
+            <el-input v-model="info.team_leader" placeholder="请填写带队领导"></el-input>
+          </form-item>
+          <form-item label="带队领导职务">
+            <el-input v-model="info.leader_position" placeholder="请填写带队领导职务"></el-input>
+          </form-item>
+          <form-item label="招聘对接联系人">
+            <el-input v-model="info.contact_person" placeholder="请填写招聘对接联系人"></el-input>
+          </form-item>
+          <form-item label="对接联系人电话">
+            <el-input v-model="info.contact_mobile" placeholder="请填写对接联系人电话"></el-input>
+          </form-item>
+          <form-item label="地址">
+            <el-input v-model="info.address" placeholder="请填写地址"></el-input>
+          </form-item>
+          <form-item label="招聘公函模板">
+            <el-link href="http://yun-campus-res.oss-cn-shenzhen.aliyuncs.com/yxqqnn0100000011/setting/1521077641-1574.doc" type="success">
+              关于赴高校开展招聘及相关工作函
+            </el-link>
+          </form-item>
+          <form-item label="招聘公函">
+            <!-- <el-upload class="upload-demo" drag action="https://jsonplaceholder.typicode.com/posts/" multiple>
+              <i class="el-icon-upload"></i>
+              <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+              <div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
+            </el-upload> -->
+            <upload-file
+              :url="`/files/jobs/talk_official/upload`"
+              desc="点击上传公函,公函不能超过2MB"
+              :limit="1"
+              @upload="uploadSuccess"
+              @changeName="changeName"
+              @toRemove="toRemove"
+              type="official_letter"
+              :data="uploads.official_letter"
+            ></upload-file>
+          </form-item>
+          <el-row type="flex" justify="center">
+            <el-col :span="6">
+              <el-button type="success" style="width:60%" @click="toSubmit()">
+                <span v-if="info.id">保&nbsp;&nbsp;&nbsp;&nbsp;存</span>
+                <span v-else>申&nbsp;&nbsp;&nbsp;&nbsp;请</span>
+              </el-button>
+            </el-col>
+          </el-row>
+        </el-form>
+        <el-dialog title="选择职位" :visible.sync="dialogSearchVisible">
+          <el-form :model="jobsForm">
+            <el-form-item>
+              <el-table :data="jobChooseList" border style="width: 100%" max-height="300">
+                <el-table-column type="index" label="序号" width="80" align="center"> </el-table-column>
+                <el-table-column prop="job_name" label="职位名称" width="400" align="left"> </el-table-column>
+                <el-table-column fixed="right" label="操作" width="100">
+                  <template v-slot="scoped">
+                    <el-button type="text" @click="jobsSel(scoped.$index, scoped.row)" size="small">选择</el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </el-form-item>
+          </el-form>
+        </el-dialog>
+        <el-dialog title="新增职位" width="70%" :visible.sync="dialogAddVisible">
+          <label style="color:red;">请如实填写职位信息,如填写的内容与实际不符,而被学生投诉,将会影响贵单位的企业信用,以及来校的招聘业务。</label>
+          <job :isDialog="true"></job>
+        </el-dialog>
+      </template>
+    </detail-layout>
+  </div>
+</template>
+
+<script>
+import detailLayout from '@/layout/detail-layout.vue';
+import backBar from '@/components/back-bar.vue';
+import job from '@/views/manager/jobs/detail.vue';
+import formItem from '@/components/form-item.vue';
+import uploadFile from '@/components/upload-file.vue';
+import { mapActions, mapState } from 'vuex';
+import _ from 'lodash';
+export default {
+  name: 'talk-detail',
+  props: {
+    original: { type: Object, default: () => {} },
+  },
+  components: {
+    formItem,
+    job,
+    backBar,
+    detailLayout,
+    uploadFile,
+  },
+  data: () => ({
+    info: {},
+    loading: true,
+    uploadUrl: `/files/jobs/talk_official/upload`,
+    uploads: {},
+    query: {},
+    schoolId: '',
+    schoolName: '',
+    profileList: [],
+    jobChooseList: [],
+    OrJobList: [],
+    schList: [],
+    recommend_web_list: [{ recommend_web: '1', recommend_web_name: '智联招聘' }, { recommend_web: '2', recommend_web_name: '51Job' }],
+    jobList: [],
+    career_time_list: [
+      { career_time: '08:30-10:00' },
+      { career_time: '10:30-12:00' },
+      { career_time: '13:00-14:30' },
+      { career_time: '15:00-16:30' },
+      { career_time: '18:00-20:30' },
+    ],
+    dialogSearchVisible: false,
+    dialogAddVisible: false,
+    jobsForm: {},
+    type_list: [{ type_value: '0', type_label: '校招' }, { type_value: '1', type_label: '实习' }, { type_value: '2', type_label: '社招' }],
+  }),
+  created() {
+    this.search();
+    this.otherList();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['profilesOperation', 'postTalksInfo', 'postOperation', 'corpSchInfo']),
+    async search() {
+      //宣讲会标题默认企业名称
+      this.$set(this.info, `title`, JSON.parse(JSON.stringify(this.user.corpname)));
+      // this.info.title = JSON.parse(JSON.stringify(this.user.corpname));
+      if (this.$route.query.id) {
+        let result = await this.postTalksInfo({ type: 'search', data: { id: this.$route.query.id } });
+        this.$set(this, `info`, result.data);
+        this.$set(this.query, `schid`, this.info.schid);
+        this.$set(this.query, `schname`, this.info.schname);
+        this.$set(this.uploads, `official_letter`, this.info.official_letter);
+        this.$set(this, `jobList`, result.data.jobs);
+      }
+    },
+    async toSubmit() {
+      let result;
+      let jobs = this.jobList.map(item => item.id);
+      let info = JSON.parse(JSON.stringify(this.info));
+      let letter = JSON.parse(JSON.stringify(this.uploads));
+      info.jobs = jobs;
+      info.official_letter = letter.official_letter;
+      if (info.id) {
+        result = await this.postTalksInfo({ type: 'update', data: { info: info, id: this.$route.query.id } });
+      } else {
+        result = await this.postTalksInfo({
+          type: 'add',
+          data: { info: info, corpid: this.user.corpid, corpname: this.user.corpname, ...this.query },
+        });
+      }
+      if (`${result.errcode}` === '0') {
+        this.$message.success('操作成功');
+        this.$router.push('/jobs/talk/index');
+      } else {
+        this.$message.error(result.errmsg);
+      }
+    },
+    // 选择职位
+    jobsSel(index, val) {
+      let res = this.jobList.filter(item => item.id === val.id);
+      if (res.length <= 0) {
+        this.jobList.push(val);
+      }
+      this.resetJobList();
+      this.dialogSearchVisible = false;
+    },
+    delJobs(index, val) {
+      this.jobList.splice(index, 1);
+      this.resetJobList();
+    },
+    selectSch(select) {
+      let res = this.schList.filter(item => {
+        return `${item.schid}` === `${select}`;
+      });
+      if (res.length > 0) {
+        this.$set(this.query, `schname`, res[0].schname);
+      }
+    },
+    async getProfile(select) {
+      let result = await this.profilesOperation({ type: 'search', data: { id: select } });
+      if (`${result.errcode}` === `0`) {
+        this.$set(this.info, `content`, result.data.content);
+      }
+    },
+    async otherList() {
+      let result = await this.postOperation({ type: 'list', data: { corpid: this.user.corpid } });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result。data的值
+        this.$set(this, `jobChooseList`, result.data);
+        this.$set(this, `OrJobList`, result.data);
+        this.resetJobList();
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+      result = await this.profilesOperation({ type: 'list', data: { corpid: this.user.corpid } });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result。data的值
+        this.$set(this, `profileList`, result.data);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+      result = await await this.corpSchInfo({ type: 'list', data: { corpid: this.user.corpid } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `schList`, result.data);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    //重置职位列表
+    async resetJobList() {
+      let data = JSON.parse(JSON.stringify(this.OrJobList));
+      let selectList = [];
+      let chooseList = [];
+      if (this.jobList.length > 0) {
+        this.jobList.map(item => {
+          let val = typeof item === 'object' ? item.id : item;
+          let selected = _.find(data, select => select.id === val);
+          selectList.push(selected);
+        });
+        // chooseList = _.differenceBy(data, selectList, 'id');
+        chooseList = _.differenceWith(data, selectList, (item, value) => {
+          return item.id === value.id;
+        });
+      } else {
+        chooseList = data;
+      }
+      this.$set(this, `jobList`, selectList);
+      this.$set(this, `jobChooseList`, chooseList);
+    },
+    uploadSuccess({ type, data }) {
+      if (type !== 'avatar_url') {
+        let arr = _.get(this.uploads, type);
+        if (arr !== undefined) {
+          if (type === 'official_letter') {
+            this.$set(this.uploads, `${type}`, { name: data.name, uri: data.uri });
+            // this.uploads[type] = { name: data.name, uri: data.uri };
+          } else {
+            this.uploads[type].push({ name: data.name, uri: data.uri });
+          }
+        } else {
+          if (type === 'official_letter') {
+            // this.uploads[type] = { name: data.name, uri: data.uri };
+            this.$set(this.uploads, `${type}`, { name: data.name, uri: data.uri });
+          } else {
+            let newArr = [{ name: data.name, uri: data.uri }];
+            this.$set(this.uploads, `${type}`, newArr);
+          }
+        }
+      } else {
+        this.$set(this.info, `${type}`, data.uri);
+      }
+    },
+    changeName({ type, data }) {
+      let newObject = { name: data.name, uri: `${data.url}` };
+      this.$set(this.uploads, `official_letter`, newObject);
+    },
+    toRemove({ type, data }) {
+      if (type !== 'avatar_url') {
+        let arr = _.get(this.uploads, type);
+        if (arr !== undefined && typeof arr === 'object' && arr.length > 0) {
+          let newArr = arr.filter(item => item.uri !== data.url);
+          this.$set(this.uploads, `${type}`, newArr);
+        }
+        if (this.info.id) {
+          this.toSubmit();
+        }
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+li {
+  list-style: none;
+}
+p {
+  margin: 0;
+  padding: 0;
+}
+.form_content {
+  padding: 20px 0;
+}
+.tit h4 {
+  color: #333;
+  font-size: 15px;
+}
+.search {
+  width: 100%;
+  float: left;
+  height: 33px;
+  margin-bottom: 10px;
+  color: #333;
+  font-size: 15px;
+  margin-bottom: 10px;
+}
+/deep/.searchInput .el-input__inner {
+  height: 33px;
+  line-height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+}
+/deep/.searchBtn .el-button {
+  width: 35px;
+  height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+  padding: 6px 7px;
+  color: #555555;
+  font-weight: bold;
+  font-size: 15px;
+  background-color: #eeeeee;
+}
+
+.talkli {
+  float: left;
+}
+.row_pagination {
+  margin-top: 1rem;
+}
+.CagegoryBox {
+  position: absolute;
+  width: 94%;
+  height: 340px;
+  padding: 20px;
+  overflow: auto;
+  background-color: #fff;
+  border: 1px solid #85bee5;
+  box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.3);
+  display: flex;
+  flex-direction: column;
+  z-index: 99999;
+}
+.CagegoryListBox li {
+  display: inline-block;
+  text-align: left;
+  width: 100px;
+  padding: 6px 3px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  cursor: pointer;
+}
+#majors {
+  margin-top: 25px;
+  border: 1px solid #ddd;
+}
+#majors .category {
+  min-width: 70px;
+  max-width: 180px;
+  padding: 0 10px;
+  background-color: #eee;
+  float: left;
+}
+#majors .list {
+  padding: 10px 20px 0;
+  overflow: auto;
+  height: 100%;
+  width: 70%;
+  float: left;
+}
+</style>

+ 200 - 0
src/views/jobs/talk/index.vue

@@ -0,0 +1,200 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template v-slot:title>
+        宣讲会
+      </template>
+      <template v-slot:functionBar>
+        <el-col :span="6">
+          <el-select v-model="searchInfo.status" placeholder="选择审核状态">
+            <el-option label="全部类型" :value="undefined"></el-option>
+            <el-option label="未审核" value="0"></el-option>
+            <el-option label="审核通过" value="1"></el-option>
+            <el-option label="审核失败" value="2"></el-option>
+          </el-select>
+        </el-col>
+        <el-col :span="2">
+          <el-button type="info" circle icon="el-icon-search icons" @click="search()"></el-button>
+        </el-col>
+      </template>
+      <template v-slot:addBtn>
+        <el-button @click="$router.push('/jobs/talk/detail')" type="success" icon="el-icon-plus">申请</el-button>
+      </template>
+      <template v-slot:main>
+        <el-row :span="24" class="Name">
+          <el-table :data="list" border style="width: 100%">
+            <el-table-column fixed type="index" label="序号" width="50" align="center"> </el-table-column>
+            <el-table-column prop="title" align="center" label="宣讲会标题"> </el-table-column>
+            <el-table-column prop="date" align="center" label="宣讲日期" sortable> </el-table-column>
+            <el-table-column prop="time" align="center" label="宣讲时间" sortable> </el-table-column>
+            <el-table-column prop="status" align="center" label="状态" sortable>
+              <template v-slot="scoped">
+                {{ `${scoped.row.status}` === '0' ? '未审核' : `${scoped.row.status}` === '1' ? '审核通过' : '审核失败' }}
+              </template>
+            </el-table-column>
+            <el-table-column align="center" label="操作" fixed="right">
+              <template v-slot="scoped">
+                <el-button @click="$router.push({ path: '/jobs/talk/detail', query: { id: scoped.row.id } })" type="text" size="mini">修改</el-button>
+                <el-button size="mini" type="text" @click="handleDelete(scoped.$index, scoped.row)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <el-row class="row_pagination">
+            <el-col :span="24">
+              <el-pagination
+                @current-change="search"
+                :current-page="currentPage"
+                :page-size="$limit"
+                layout="total, prev, pager, next, jumper"
+                :total="totalRow"
+              >
+              </el-pagination>
+            </el-col>
+          </el-row>
+        </el-row>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {
+    listNormal,
+  },
+  data: () => ({
+    currentPage: 1,
+    totalRow: 0,
+    list: [],
+    searchInfo: {},
+  }),
+  created() {
+    // this.totalRow = this.datalist.length;
+    this.search();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['postTalksInfo']),
+    async search(val) {
+      let skip = 0;
+      if (val) {
+        this.currentPage = val;
+        skip = (this.currentPage - 1) * this.$limit;
+      }
+      let result = await this.postTalksInfo({
+        type: 'list',
+        data: { schid: '福瑞', corpid: this.user.corpid, corpname: '福瑞test', schname: '福瑞', skip: skip, limit: this.$limit, ...this.searchInfo },
+      });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result。data的值
+        this.$set(this, 'list', result.data);
+        this.$set(this, `totalRow`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    async handleDelete(index, item) {
+      this.$confirm('是否删除该宣讲会?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.toDelete(item.id);
+        })
+        .catch(() => {});
+    },
+    async toDelete(id) {
+      let result = await this.postTalksInfo({ type: 'delete', data: { id: id } });
+      if (`${result.errcode}` === '0') {
+        this.$message.success('删除成功');
+        this.search();
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : '删除失败');
+      }
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.search();
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+li {
+  list-style: none;
+}
+p {
+  margin: 0;
+  padding: 0;
+}
+.tit h4 {
+  color: #333;
+  font-size: 15px;
+}
+.search {
+  width: 100%;
+  float: left;
+  height: 33px;
+  margin-bottom: 10px;
+  color: #333;
+  font-size: 15px;
+  margin-bottom: 10px;
+}
+/deep/.searchInput .el-input__inner {
+  height: 33px;
+  line-height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+}
+/deep/.searchBtn .el-button {
+  width: 35px;
+  height: 33px;
+  border: 1px solid #ccc;
+  border-radius: 0px;
+  padding: 6px 7px;
+  color: #555555;
+  font-weight: bold;
+  font-size: 15px;
+  background-color: #eeeeee;
+}
+.add {
+  float: right;
+  width: 69px;
+  height: 34px;
+}
+.add .el-button {
+  float: right;
+  padding: 0;
+  width: 69px;
+  height: 34p;
+  height: 34px;
+  text-align: center;
+  background: #44b549;
+  color: #fff;
+  font-size: 14px;
+  font-weight: bold;
+}
+.talkli {
+  float: left;
+}
+.Name {
+  float: left;
+  width: 100%;
+}
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+/deep/.el-table--enable-row-transition .el-table__body td {
+  text-align: center;
+}
+</style>

+ 99 - 0
src/views/manager/deliver/index.vue

@@ -0,0 +1,99 @@
+<template>
+  <div id="index">
+    <list-tab :tab="tabsTitle">
+      <template v-slot:title>
+        投递简历
+      </template>
+      <template v-slot:main1>
+        <list type="0" :list="list0" :totalRow="totalRow0" @changePage="search" @selectInfo="selectInfo" @changeStatus="changeStatus"></list>
+      </template>
+      <template v-slot:main2>
+        <list type="1" :list="list1" :totalRow="totalRow1" @changePage="search" @selectInfo="selectInfo" @changeStatus="changeStatus"></list>
+      </template>
+      <template v-slot:main3>
+        <list type="2" :list="list2" :totalRow="totalRow2" @changePage="search" @selectInfo="selectInfo" @changeStatus="changeStatus"></list>
+      </template>
+    </list-tab>
+  </div>
+</template>
+
+<script>
+import list from './list.vue';
+import listTab from '@/layout/list-tab.vue';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'rindex',
+  props: {},
+  components: {
+    listTab,
+    list,
+  },
+  data: () => ({
+    tabsTitle: ['未处理', '纳入考虑', '暂不考虑'],
+    searchName: '',
+    list: [],
+    info: {},
+    list0: [],
+    list1: [],
+    list2: [],
+    totalRow0: 0,
+    totalRow1: 0,
+    totalRow2: 0,
+  }),
+  created() {
+    this.search({ type: '0' });
+    this.search({ type: '1' });
+    this.search({ type: '2' });
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['lettersOperation']),
+    async search({ type, currentPage, searchInfo }) {
+      let skip = 0;
+      if (currentPage) {
+        skip = (currentPage - 1) * this.$limit;
+      }
+      let result = await this.lettersOperation({
+        type: 'list',
+        data: { post_id: this.$route.query.id, status: type, skip: skip, limit: this.$limit, corpname: this.user.corpname, ...searchInfo },
+      });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `list${type}`, result.data);
+        this.$set(this, `totalRow${type}`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    async searchInfo(id) {
+      let result = await this.lettersOperation({ type: 'search', data: { id: id } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `info`, result.data);
+      }
+    },
+    async changeStatus({ id, status }) {
+      let result = await this.lettersOperation({ type: 'update', data: { id: id, status: status } });
+      if (`${result.errcode}` === '0') {
+        this.$message.success('操作成功');
+      } else {
+        this.$message.error(result.errmsg || 'error');
+      }
+    },
+    changePage({ type, currentPage }) {
+      //type=>哪个标签页
+      //currentPage=>标签页的当前页,用来计算skip的
+      console.log(type, currentPage);
+      //根据type查询对应的数据
+    },
+    selectInfo({ type, ids }) {
+      //type=>哪个标签页
+      //ids,选择的id
+      console.log(type, ids);
+      //缓存在本组件中(this.$set在本组件中),为之后的批量操作使用
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 127 - 0
src/views/manager/deliver/list.vue

@@ -0,0 +1,127 @@
+<template>
+  <div id="list">
+    <el-row :gutter="20" style="padding-bottom:1.2rem;">
+      <el-col :span="6">
+        <el-input v-model="searchInfo.studname" placeholder="请输入学生名称"></el-input>
+      </el-col>
+      <el-col :span="2">
+        <el-button type="info" circle icon="el-icon-search icons" @click="handleCurrentChange"></el-button>
+      </el-col>
+    </el-row>
+    <el-table :data="list" stripe border style="width: 100%">
+      <el-table-column align="center" type="selection" width="40"> </el-table-column>
+      <el-table-column align="center" type="index" prop="num" label="序号" width="50"></el-table-column>
+      <el-table-column align="center" prop="data" label="头像">
+        <div class="demo-basic--circle">
+          <div class="block"><el-avatar :size="80" :src="circleUrl"></el-avatar></div>
+        </div>
+      </el-table-column>
+      <el-table-column align="center" prop="date" label="基本信息">
+        <el-row><el-button type="text">姓名</el-button></el-row>
+        <el-row>性别|学校</el-row>
+        <el-row>学历|专业</el-row>
+      </el-table-column>
+      <el-table-column align="center" prop="date" label="投递职位"></el-table-column>
+      <el-table-column align="center" prop="data" label="投递日期"></el-table-column>
+      <el-table-column align="center" prop="data" label="简历状态">
+        <template v-slot="scoped">
+          <el-row v-if="type == '1'" type="text">未处理</el-row>
+          <el-row v-if="type == '2'" type="text">合格</el-row>
+          <el-row v-if="type == '3'" type="text">暂不考虑</el-row>
+        </template>
+      </el-table-column>
+      <el-table-column align="center" prop="data" label="操作">
+        <template v-slot="scoped">
+          <el-row>
+            <el-col :span="24">
+              <el-button size="mini" type="text" @click="checkResume(scoped.row.id)">查看简历</el-button>
+            </el-col>
+            <el-col :span="12">
+              <el-button size="mini" v-if="type !== '2'" type="text" @click="changeStatus('1')">纳入考虑</el-button>
+            </el-col>
+            <el-col :span="12">
+              <el-button size="mini" v-if="type !== '3'" type="text" @click="changeStatus('2')">暂不考虑</el-button>
+            </el-col>
+            <el-col :span="12">
+              <el-button size="mini" type="text">查看回复</el-button>
+            </el-col>
+            <el-col :span="12">
+              <el-button size="mini" type="text">通知</el-button>
+            </el-col>
+          </el-row>
+          <el-row> </el-row>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-row class="row_pagination">
+      <el-col :span="24">
+        <el-pagination
+          @current-change="handleCurrentChange"
+          :current-page="currentPage"
+          :page-size="15"
+          layout="total, prev, pager, next, jumper"
+          :total="totalRow"
+        >
+        </el-pagination>
+      </el-col>
+    </el-row>
+    <el-dialog title="简历详情" center :visible.sync="resumeDialog" :fullscreen="true">
+      <resumes :info="info"></resumes>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import resumes from '@resume/src/views/resumeCor.vue';
+export default {
+  name: 'list',
+  props: {
+    type: { type: String, defalut: 'none' },
+    list: { type: Array, defalut: () => [] },
+    totalRow: { type: Number, defalut: 0 },
+    info: { type: Object, default: () => {} },
+  },
+  components: {
+    resumes,
+  },
+  data: () => ({
+    currentPage: 1,
+    resumeDialog: false,
+    circleUrl: '',
+    searchInfo: {},
+  }),
+  created() {},
+  computed: {},
+  methods: {
+    // selectInfo(selection) {
+    //   //选择/全选方法,删除或者其他批量操作使用
+    //   let ids = [];
+    //   selection.map(item => {
+    //     ids.push(item.id);
+    //   });
+    //   this.$emit('selectInfo', { type: this.type, ids: ids });
+    // },
+    handleCurrentChange(val) {
+      this.currentPage = val ? 1 : val;
+      this.$emit('changePage', { type: this.type, currentPage: this.currentPage, searchInfo: this.searchInfo });
+    },
+    checkResume(id) {
+      this.$emit('searchInfo', id);
+      this.resumeDialog = true;
+    },
+    changeStatus(value) {
+      this.$emit('changeStatus', { id: this.info.id, status: value });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+/deep/.el-dialog__body {
+  padding: 0;
+}
+</style>

+ 161 - 0
src/views/manager/invite/index.vue

@@ -0,0 +1,161 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template v-slot:title>
+        <!-- 此处v-slot:title后可以加="data",data代表将slot传来的参数再次封装后的key=>{data:params}(data可以随意替换),
+        也就是说,想取到最后的数据集,我们需要解开1层object,(template声明的名称).(slot :xxx绑定名称的 xxx)才能取到数据 -->
+        <!-- 当然,我们现在没涉及用上述功能,只是就想搞明白而已╮(╯▽╰)╭ -->
+        <!-- 如果组件文件夹下的needOrNot文件夹的组件可以使用,那么这部分就应该需要理解了,因为操作按钮部分是需要自己写 -->
+        人才邀约
+      </template>
+      <template v-slot:functionBar>
+        <el-col :span="4">
+          <search v-model="select" type="select" :dataList="testList"></search>
+        </el-col>
+        <el-col :span="4">
+          <search v-model="select" type="select" placeholder="2019"></search>
+        </el-col>
+        <el-col :span="4">
+          <search v-model="input" placeholder="请输入意向城市"></search>
+        </el-col>
+        <el-col :span="4">
+          <search v-model="input" placeholder="请输入意向职位"></search>
+        </el-col>
+        <el-col :span="4">
+          <search v-model="input" placeholder="请输入专业"></search>
+        </el-col>
+        <el-col :span="2">
+          <el-button type="info" circle icon="el-icon-search icons"></el-button>
+        </el-col>
+      </template>
+      <template v-slot:main>
+        <el-table :data="dataList" stripe border style="width: 100%">
+          <el-table-column type="index" align="center" prop="num" label="序号" width="50"> </el-table-column>
+          <el-table-column align="center" prop="picture" label="头像">
+            <div class="demo-basic--circle">
+              <div class="block"><el-avatar :size="80" :src="circleUrl"></el-avatar></div>
+            </div>
+          </el-table-column>
+          <el-table-column prop="date" label="基本信息">
+            <template v-slot="scope">
+              <el-row>
+                <el-col :span="24">
+                  <!-- 链接简历模块 -->
+                  <el-button type="text">姓名</el-button>
+                </el-col>
+                <el-col :span="24">
+                  性别
+                </el-col>
+                <el-col :span="24">
+                  毕业学校
+                </el-col>
+                <el-col :span="24">
+                  专业
+                </el-col>
+              </el-row>
+            </template>
+          </el-table-column>
+          <el-table-column prop="name" label="求职意向">
+            <el-row>
+              <el-col :span="24">
+                城市
+              </el-col>
+              <el-col :span="24">
+                工资
+              </el-col>
+              <el-col :span="24">
+                职位
+              </el-col>
+            </el-row>
+          </el-table-column>
+          <el-table-column align="center" prop="address" label="操作">
+            <template slot-scope="scope">
+              <el-button @click="handleClick(scope.row)" type="text" size="small">查看简历</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <el-row class="row_pagination">
+          <el-col :span="24">
+            <el-pagination
+              @current-change="handleCurrentChange"
+              :current-page="currentPage"
+              :page-size="15"
+              layout="total, prev, pager, next, jumper"
+              :total="totalRow"
+            >
+            </el-pagination>
+          </el-col>
+        </el-row>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import search from '@/components/search.vue';
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  components: {
+    listNormal,
+    search,
+  },
+  data: () => ({
+    testList: [{ label: 'test', value: 1 }, { label: 'name', value: 2 }, { label: 'lock', value: 3 }, { label: 'not', value: 4 }],
+    dataList: [],
+    select: '',
+    circleUrl: '',
+    sizeList: '',
+    input: '',
+    listSelect: [],
+    currentPage: 1,
+    totalRow: 60,
+  }),
+  created() {
+    this.search();
+  },
+  methods: {
+    ...mapActions(['getList']),
+    test() {
+      console.log('in function:');
+    },
+    async search(val) {
+      let skip = 0;
+      if (val) {
+        this.currentPage = val;
+        skip = (this.currentPage - 1) * this.$limit;
+      }
+      let result = await this.getList();
+      this.$set(this, `dataList`, result);
+    },
+    selectInfo(selection) {
+      //选择/全选方法,删除或者其他批量操作使用
+      let ids = [];
+      selection.map(item => {
+        ids.push(item.id);
+      });
+      this.$set(this, `listSelect`, ids);
+      console.log(ids);
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.search();
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.icons {
+  zoom: 2;
+  position: relative;
+  top: 0.5rem;
+}
+</style>

+ 382 - 0
src/views/manager/jobs/detail.vue

@@ -0,0 +1,382 @@
+<template>
+  <div id="detail">
+    <detail-layout>
+      <template #title v-if="!isDialog">
+        <back-bar to="/manager/jobs/index" title="职位详情"></back-bar>
+      </template>
+      <template #main>
+        <el-row>
+          <el-form :model="info">
+            <form-item label="企业名称">
+              {{ user.corpname }}
+            </form-item>
+            <form-item label="职位名称" prop="job_name">
+              <el-input placeholder="每个职位职能录入一个岗位" v-model="info.job_name"></el-input>
+            </form-item>
+            <form-item label="职位分类">
+              <el-button @click="drawer = true" style="width:100%;">{{ info.category ? info.category : '点击选择职位' }} </el-button>
+              <el-drawer title="选择职位" :visible.sync="drawer" :direction="`rtl`">
+                <el-row>
+                  <el-col :span="24">
+                    <el-button v-for="(item, index) in jobTypeList" :key="index" @click="selectJobs(item)" size="small" style="margin: 0 0.5rem;">
+                      {{ item.label }}
+                    </el-button>
+                  </el-col>
+                </el-row>
+              </el-drawer>
+            </form-item>
+            <form-item label="工作城市">
+              <tag-all-select
+                placeholder="点击选择城市"
+                :firstList="provinceList"
+                :secondList="cityList"
+                @selectChange="selectChange"
+                @listChange="listChange"
+                :selected="info.city"
+                type="city"
+              ></tag-all-select>
+            </form-item>
+            <form-item label="工作性质">
+              <el-radio-group v-model="info.is_practice" size="small">
+                <el-radio v-for="(item, index) in type_list" :key="index" :label="item.type_value" border>{{ item.type_label }}</el-radio>
+              </el-radio-group>
+            </form-item>
+            <form-item label="学历要求">
+              <el-select v-model="info.xl_req" placeholder="请选择学历要求">
+                <el-option v-for="(item, index) in edu_list" :key="index" :label="item.label" :value="item.label"></el-option>
+              </el-select>
+            </form-item>
+            <form-item label="职位诱惑">
+              <tagCheckbox
+                placeholder="点击选择职位诱惑"
+                :firstList="entice_list"
+                @selectChange="selectChange"
+                @listChange="listChange"
+                :selected="info.job_tag"
+                type="job_tag"
+              ></tagCheckbox>
+            </form-item>
+            <form-item label="招聘人数">
+              <el-input-number v-model="info.job_number" :min="1" placeholder="添加需要招聘的人数" style="width:100%"></el-input-number>
+            </form-item>
+            <form-item label="截止日期">
+              <el-date-picker v-model="info.end_date" type="date" placeholder="选择日期" format="yyyy 年 MM 月 dd 日" value-format="yyyy-MM-dd">
+              </el-date-picker>
+            </form-item>
+            <form-item label="薪资">
+              <el-col :span="6">
+                <el-input type="number" v-model="info.salary.min">
+                  <template #append>
+                    <strong>K</strong>
+                  </template>
+                </el-input>
+              </el-col>
+              <el-col :span="6" style="text-align:center;">
+                至
+              </el-col>
+              <el-col :span="6">
+                <el-input type="number" v-model="info.salary.max">
+                  <template #append>
+                    <strong>K</strong>
+                  </template>
+                </el-input>
+              </el-col>
+            </form-item>
+            <form-item label="薪酬福利">
+              <el-col :span="8" v-for="(item, index) in welfare_list" :key="index">
+                <el-radio v-model="info.welfare" :label="item.value" :border="true" size="small">
+                  {{ item.label }}
+                </el-radio>
+              </el-col>
+            </form-item>
+            <form-item label="相关专业">
+              <tagSecSelect
+                placeholder="点击选择相关专业"
+                :selected="info.zy_req"
+                :firstList="subjectList"
+                :secondList="subjectSubList"
+                @selectChange="selectChange"
+                @listChange="listChange"
+                type="zy_req"
+              ></tagSecSelect>
+            </form-item>
+            <form-item label="职位描述">
+              <el-input type="textarea" :rows="5" placeholder="请输入职位描述" v-model="info.job_desc"> </el-input>
+            </form-item>
+            <form-item label="简历投递说明">
+              <el-input type="textarea" :rows="5" placeholder="请输入简历投递说明" v-model="info.apply_intro"> </el-input>
+            </form-item>
+            <form-item label="岗位要求">
+              <el-input type="textarea" :rows="5" placeholder="请输入岗位要求" v-model="info.job_req"> </el-input>
+            </form-item>
+            <form-item label="其他描述">
+              <el-input type="textarea" :rows="5" placeholder="请输入其他描述" v-model="info.other"> </el-input>
+            </form-item>
+
+            <el-row type="flex" align="middle" justify="center">
+              <el-col :span="6">
+                <el-button type="success" style="width:100%; margin: 1rem 0;" @click="toSubmit">提&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;交</el-button>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-row>
+      </template>
+    </detail-layout>
+  </div>
+</template>
+
+<script>
+import backBar from '@/components/back-bar.vue';
+import tagSecSelect from '@/components/tag-sec-select.vue';
+import tagCheckbox from '@/components/tag-checkbox.vue';
+import tagAllSelect from '@/components/tag-all-select.vue';
+import formItem from '@/components/form-item.vue';
+import detailLayout from '@/layout/detail-layout.vue';
+import { mapActions, mapState } from 'vuex';
+
+export default {
+  name: 'detail',
+  props: {
+    isDialog: { type: Boolean, default: false },
+  },
+  components: {
+    detailLayout,
+    formItem,
+    tagAllSelect,
+    tagCheckbox,
+    tagSecSelect,
+    backBar,
+  },
+  data: () => ({
+    info: {
+      salary: {
+        min: '',
+        max: '',
+      },
+    },
+    jobType: '',
+    drawer: false,
+    jobTypeList: [
+      {
+        label: 'IT',
+        value: '1',
+      },
+      {
+        label: 'test2',
+        value: '2',
+      },
+      {
+        label: 'test3',
+        value: '3',
+      },
+      {
+        label: 'test4',
+        value: '4',
+      },
+    ],
+    provinceList: [{ label: '北京市', value: '110000' }, { label: '吉林省', value: '220000' }, { label: '沈阳省', value: '210000' }],
+    secondList: [{ label: '长春市', value: '220100' }, { label: '吉林市', value: '220200' }],
+    secondList1: [{ label: '沈阳市', value: '210100' }, { label: '大连市', value: '210200' }],
+    cityList: [],
+    type_list: [{ type_value: 0, type_label: '全职' }, { type_value: 1, type_label: '实习' }],
+    edu_list: [
+      { value: '不限', label: '不限' },
+      { value: '中专及以上', label: '中专及以上' },
+      { value: '大专及以上', label: '大专及以上' },
+      { value: '本科及以上', label: '本科及以上' },
+      { value: '硕士及以上', label: '硕士及以上' },
+      { value: '博士及以上', label: '博士及以上' },
+    ],
+    entice_list: [
+      { value: '1', label: '年底双薪' },
+      { value: '2', label: '绩效奖金' },
+      { value: '3', label: '岗前培训' },
+      { value: '4', label: '节日礼物' },
+      { value: '5', label: '扁平管理' },
+      { value: '6', label: '年度旅游' },
+      { value: '7', label: '岗位晋升' },
+      { value: '8', label: '股票期权' },
+      { value: '9', label: '弹性工作' },
+      { value: '10', label: '带薪年假' },
+      { value: '11', label: '交通补助' },
+      { value: '12', label: '餐补' },
+      { value: '13', label: '房补' },
+      { value: '14', label: '免费班车' },
+      { value: '15', label: '员工旅游' },
+      { value: '16', label: '包吃包住' },
+      { value: '17', label: '健康体检' },
+      { value: '18', label: '留人基金' },
+      { value: '19', label: '学费返还' },
+    ],
+    welfare_list: [
+      { value: '无', label: '无' },
+      { value: '五险一金', label: '五险一金' },
+      { value: '三险一金', label: '三险一金' },
+      { value: '六险一金', label: '六险一金' },
+      { value: '五险', label: '五险' },
+      { value: '三险', label: '三险' },
+    ],
+    subjectList: [
+      { label: '不限专业', value: '0' },
+      { label: '哲学', value: '1' },
+      { label: '经济学', value: '2' },
+      { label: '法学', value: '3' },
+      { label: '教育学', value: '4' },
+      { label: '文学', value: '5' },
+      { label: '历史学', value: '6' },
+      { label: '理学', value: '7' },
+      { label: '工学', value: '8' },
+      { label: '农学', value: '9' },
+      { label: '医学', value: '10' },
+    ],
+    subjectSubList: [],
+    subjectSub: [
+      { label: '哲学类', value: '11' },
+      { label: '哲学', value: '12' },
+      { label: '逻辑类', value: '13' },
+      { label: '宗教类', value: '14' },
+      { label: '伦理类', value: '15' },
+    ],
+    subjectSub2: [{ label: '社会经济学', value: '16' }, { label: '国际经济学', value: '17' }, { label: '资本主义经济学', value: '18' }],
+  }),
+  created() {
+    if (this.$route.query.id) {
+      this.search();
+    }
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['postOperation']),
+    async search() {
+      let result = await this.postOperation({ type: 'search', data: { id: this.$route.query.id } });
+      let infos = JSON.parse(JSON.stringify(result.data));
+      this.$set(this, `info`, infos);
+      this.returnData(infos);
+    },
+    selectChange({ val, type }) {
+      //此方法是同步选择
+      this.$set(this.info, type, val);
+    },
+    listChange({ val, type }) {
+      //此方法是更换子列表的
+      if (val === '220000') {
+        this.$set(this, `cityList`, this.secondList);
+      } else if (val === '210000') {
+        this.$set(this, `cityList`, this.secondList1);
+      } else if (type === 'zy_req') {
+        //专业查询模拟
+        this.$set(this, `subjectSubList`, val === '1' ? this.subjectSub : this.subjectSub2);
+      }
+    },
+    async toSubmit() {
+      let result;
+      let infos = JSON.parse(JSON.stringify(this.info));
+      let mid = '';
+      infos.salary.text = infos.salary.min + 'k-' + infos.salary.max + 'k/月';
+      if (typeof infos.city === 'object') {
+        infos.city.map(item => {
+          mid === '' ? (mid = item.label) : (mid += `,${item.label}`);
+        });
+        infos.city = mid;
+        mid = '';
+      }
+      if (typeof infos.job_tag === 'object') {
+        infos.job_tag.map(item => {
+          mid === '' ? (mid = item.label) : (mid += `,${item.label}`);
+        });
+        infos.job_tag = mid;
+        mid = '';
+      }
+      if (typeof infos.zy_req === 'object') {
+        infos.zy_req.map(item => {
+          mid === '' ? (mid = item.label) : (mid += `,${item.label}`);
+        });
+        infos.zy_req = mid;
+      }
+      if (this.info.id) {
+        result = await this.postOperation({ type: 'update', data: { info: infos, id: this.$route.query.id } });
+      } else {
+        result = await this.postOperation({ type: 'add', data: { info: infos, corpname: this.user.corpname, corpid: this.user.corpid } });
+      }
+      if (`${result.errcode}` === '0') {
+        this.$message.success('操作成功');
+        if (this.isDialog) {
+          this.$emit('jobSubmit', true);
+          return;
+        } else {
+          this.$router.push('/manager/jobs/index');
+        }
+      } else {
+        this.$message.error(result.errmsg);
+      }
+    },
+    selectJobs(item) {
+      this.$set(this.info, `category`, item.label);
+      this.drawer = false;
+    },
+    returnData(data) {
+      if (data.city) {
+        let mid = data.city.split(',');
+        let selected = [];
+        for (const select of mid) {
+          let res = this.provinceList.filter(item => item.label === select);
+          if (res.length > 0) {
+            selected = [...selected, ...res];
+          } else {
+            let res = this.secondList.filter(item => item.label === select);
+            if (res.length > 0) {
+              selected = [...selected, ...res];
+            } else {
+              let res = this.secondList1.filter(item => item.label === select);
+              if (res.length > 0) {
+                selected = [...selected, ...res];
+              }
+            }
+          }
+        }
+        console.log(mid);
+        console.log(selected);
+        this.$set(this.info, `city`, selected);
+      }
+      if (data.zy_req) {
+        let mid = data.zy_req.split(',');
+        let selected = [];
+        for (const select of mid) {
+          let res = this.subjectList.filter(item => item.label === select && item.value === '0');
+          if (res.length <= 0) {
+            res = this.subjectSub.filter(item => item.label === select);
+            selected = [...selected, ...res];
+            res = this.subjectSub2.filter(item => item.label === select);
+            selected = [...selected, ...res];
+            this.$set(this.info, `zy_req`, selected);
+          } else {
+            this.$set(this.info, `zy_req`, res);
+          }
+        }
+      }
+      if (data.job_tag) {
+        let mid = data.job_tag.split(',');
+        let selected = [];
+        for (const select of mid) {
+          let res = this.entice_list.filter(item => item.label === select);
+          selected = [...selected, ...res];
+        }
+        this.$set(this.info, `job_tag`, selected);
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.selfInput {
+  height: 40px;
+  border-color: #c0c4cc;
+}
+.bord {
+  background: #f4f5f9;
+  border: 1px solid #e7e8ec;
+}
+</style>

+ 172 - 0
src/views/manager/jobs/index.vue

@@ -0,0 +1,172 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template v-slot:title>
+        <el-row>
+          <el-col :span="24"> 职位管理<span style="color :#999999; font-size :12px;"></span> </el-col>
+        </el-row>
+      </template>
+      <template v-slot:functionBar>
+        <el-col :span="6">
+          <el-select v-model="searchInfo.is_practice" placeholder="选择工作性质">
+            <el-option label="全部类型" :value="undefined"></el-option>
+            <el-option label="全职" :value="0"></el-option>
+            <el-option label="实习" :value="1"></el-option>
+          </el-select>
+        </el-col>
+        <el-col :span="2">
+          <el-button type="info" circle icon="el-icon-search icons" @click="search()"></el-button>
+        </el-col>
+      </template>
+      <template v-slot:addBtn>
+        <el-button type="success" icon="el-icon-plus" @click="$router.push('/manager/jobs/detail')">新增</el-button>
+      </template>
+
+      <template v-slot:main>
+        <!-- <el-row type="flex" justify="end" style="padding:0.5rem">
+          <el-col :span="4">
+            <el-button type="success" size="small" icon="el-icon-plus" @click="toPublish()">发布职位</el-button>
+          </el-col>
+        </el-row> -->
+        <el-table :data="postList" :border="true" :stripe="true" @select="selectInfo" @select-all="selectInfo">
+          <el-table-column align="center" type="selection" width="40"> </el-table-column>
+          <el-table-column align="center" type="index" label="序号"></el-table-column>
+          <el-table-column align="center" prop="job_name" label="职位名称"></el-table-column>
+          <el-table-column align="center" prop="is_practice" label="工作性质" sortable>
+            <template v-slot="scoped">
+              {{ `${scoped.row.is_practice}` === `0` ? '全职' : '实习' }}
+            </template>
+          </el-table-column>
+          <el-table-column align="center" prop="category" label="职位分类"></el-table-column>
+          <el-table-column align="center" prop="xl_req" label="学历要求"></el-table-column>
+          <el-table-column align="center" prop="zy_req" label="专业要求"></el-table-column>
+          <el-table-column align="center" prop="job_number" label="招聘人数"></el-table-column>
+          <el-table-column align="center" prop="end_date" label="截止时间" sortable></el-table-column>
+          <el-table-column align="center" label="操作">
+            <template v-slot="scope">
+              <el-row>
+                <el-col :span="24">
+                  <el-button size="mini" type="text" @click="$router.push({ path: '/manager/jobs/resume', query: { id: scope.row.id } })">简历筛选</el-button>
+                  <el-button size="mini" type="text" @click="$router.push({ path: '/manager/jobs/detail', query: { id: scope.row.id } })">
+                    修改
+                  </el-button>
+                </el-col>
+                <el-col :span="24">
+                  <el-button v-if="`${scope.row.is_publish}` === '1'" size="mini" type="text" @click="handleDelete(scope.$index, scope.row)">
+                    取消发布
+                  </el-button>
+                  <el-button v-else size="mini" type="text" @click="toPublish(scope.row.id, scope.row.is_publish)">
+                    发布
+                  </el-button>
+                  <el-button size="mini" type="text" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
+                </el-col>
+              </el-row>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <el-row class="row_pagination">
+          <el-col :span="24">
+            <el-pagination @current-change="search" :current-page="currentPage" :page-size="$limit" layout="total, prev, pager, next, jumper" :total="totalRow">
+            </el-pagination>
+          </el-col>
+        </el-row>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  components: {
+    listNormal,
+  },
+  data: () => ({
+    testList: [{ label: '职位类型', value: 1 }, { label: '校招', value: 2 }, { label: '社招', value: 3 }, { label: '实习', value: 4 }],
+    select: '',
+    searchInfo: {},
+    listSelect: [],
+    currentPage: 1,
+    totalRow: 60,
+    postList: [],
+  }),
+  created() {
+    this.search();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['postOperation']),
+    selectInfo(selection) {
+      //选择/全选方法,删除或者其他批量操作使用
+      let ids = [];
+      selection.map(item => {
+        ids.push(item.id);
+      });
+      this.$set(this, `listSelect`, ids);
+    },
+    async search(val) {
+      let skip = 0;
+      if (val) {
+        this.currentPage = val;
+        skip = (this.currentPage - 1) * this.$limit;
+      }
+      //1直接拿着参数发送请求
+      let result = await this.postOperation({ type: 'list', data: { corpid: this.user.corpid, skip: skip, limit: this.$limit, ...this.searchInfo } });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result.data的值
+        this.$set(this, `postList`, result.data);
+        this.$set(this, `totalRow`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    async handleDelete(index, item) {
+      this.$confirm('是否删除该职位?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.toDelete(item.id);
+        })
+        .catch(() => {});
+    },
+    async toDelete(id) {
+      let result = await this.postOperation({ type: 'delete', data: { id: id } });
+      if (`${result.errcode}` === '0') {
+        this.$message.success('删除成功');
+        this.search();
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : '删除失败');
+      }
+    },
+    async toPublish(id, state) {
+      let result = await this.postOperation({ type: 'update', data: { id: id, info: { is_publish: `${state}` === '0' ? 1 : 0 } } });
+      if (`${result.errcode}` === '0') {
+        this.$message.success('发布成功');
+        this.search();
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : '发布失败');
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.icons {
+  zoom: 2;
+  position: relative;
+  top: 0.5rem;
+}
+</style>

+ 69 - 0
src/views/manager/jobs/resume.vue

@@ -0,0 +1,69 @@
+<template>
+  <div id="resume" style="padding-bottom:1.25rem">
+    <list-tab :tab="tabsTitle">
+      <template v-slot:title>
+        <back-bar to="/manager/jobs/index" title="职位管理"></back-bar>
+      </template>
+      <template v-slot:main1>
+        <resume-tem type="1" :list="list" :info="info" @search="search" @searchInfo="searchInfo" @changeStatus="changeStatus"></resume-tem>
+      </template>
+      <!-- <template v-slot:main2>
+        <resume-tem type="2"></resume-tem>
+      </template> -->
+      <template v-slot:main2>
+        <resume-tem type="3" :list="list" :info="info" @search="search" @searchInfo="searchInfo" @changeStatus="changeStatus"></resume-tem>
+      </template>
+    </list-tab>
+  </div>
+</template>
+
+<script>
+import listTab from '@/layout/list-tab.vue';
+import backBar from '@/components/back-bar.vue';
+import resumeTem from './resume/resume-tem.vue';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'resume',
+  props: {},
+  components: {
+    resumeTem,
+    listTab,
+    backBar,
+  },
+  data: () => ({
+    tabsTitle: ['简历夹', '录用夹'],
+    list: [],
+    info: {},
+  }),
+  created() {
+    this.search({ status: '0' });
+    this.search({ status: '1' });
+  },
+  computed: {},
+  methods: {
+    ...mapActions(['lettersOperation']),
+    async search({ status, studname }) {
+      let result = await this.lettersOperation({ type: 'list', data: { post_id: this.$route.query.id, status: status, studname: studname } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `list`, result.data);
+      }
+    },
+    async searchInfo(id) {
+      let result = await this.lettersOperation({ type: 'search', data: { id: id } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `info`, result.data);
+      }
+    },
+    async changeStatus({ id, status }) {
+      let result = await this.lettersOperation({ type: 'update', data: { id: id, status: status } });
+      if (`${result.errcode}` === '0') {
+        this.$message.success('操作成功');
+      } else {
+        this.$message.error(result.errmsg || 'error');
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 194 - 0
src/views/manager/jobs/resume/context.vue

@@ -0,0 +1,194 @@
+<template>
+  <div id="context">
+    <el-row v-if="info && info.id">
+      <el-col :span="24" class="interviewMainHead">
+        <el-col class="headTit">
+          <p class="headTit-text">
+            <span class="text-inline headName">{{ (info.info && info.info.name) || '名字' }}</span>
+            <span class="text-inline">{{ (info.info && info.info.gander) || '性别' }}</span>
+          </p>
+          <p class="headTit-text">
+            <span class="text-inline">籍贯:渭南</span>
+            <span class="text-inline">专业:null</span>
+            <span class="text-inline">学校:null</span>
+          </p>
+          <p class="headTit-text">
+            <span class="text-inline">手机:</span>
+            <span class="text-inline">邮箱:</span>
+          </p>
+        </el-col>
+        <el-col :span="24" class="headImg"><img :src="info.avatar_url" width="64" height="64px"/></el-col>
+      </el-col>
+      <el-col :span="24" class="interviewMainMod">
+        <el-col class="ModTit">
+          <h4>求职意向</h4>
+        </el-col>
+        <el-col class="ModTxt">
+          <ul>
+            <li></li>
+          </ul>
+        </el-col>
+      </el-col>
+      <el-col :span="24" class="interviewMainMod">
+        <el-col class="ModTit">
+          <h4>教育经历</h4>
+        </el-col>
+        <el-col class="ModTxt">
+          <ul v-for="(item, index) in info.educations" :key="index">
+            <li>{{ item.rxsj || '' }}-{{ item.bysj || '' }} {{ item.yx || '' }}-{{ item.fy || '' }}</li>
+            <li>{{ item.zy || '' }}</li>
+            <li>{{ item.xl || '' }}</li>
+          </ul>
+        </el-col>
+      </el-col>
+      <el-col :span="24" class="interviewMainMod">
+        <el-col class="ModTit">
+          <h4>专业技能</h4>
+        </el-col>
+        <el-col class="ModTxt">
+          <ul>
+            <li>{{ info.skill || '' }}</li>
+          </ul>
+        </el-col>
+      </el-col>
+      <el-col :span="24" class="interviewMainMod">
+        <el-col class="ModTit">
+          <h4>工作经历/项目经历</h4>
+        </el-col>
+        <el-col class="ModTxt">
+          <ul v-for="(item, index) in info.works" :key="index">
+            <li>{{ item.begin || '' }}-{{ item.end || '' }} {{ item.corpname || '' }}-{{ item.position || '' }}</li>
+            <li>{{ item.desc || '' }}</li>
+          </ul>
+        </el-col>
+      </el-col>
+      <el-col :span="24" class="interviewMainMod">
+        <el-col class="ModTit">
+          <h4>证书或荣誉</h4>
+        </el-col>
+        <el-col class="ModTxt">
+          <ul>
+            <li></li>
+          </ul>
+        </el-col>
+      </el-col>
+      <el-col :span="24" class="interviewMainMod">
+        <el-col class="ModTit">
+          <h4>能力证明材料</h4>
+        </el-col>
+        <el-col class="ModTxt">
+          <ul>
+            <li></li>
+          </ul>
+        </el-col>
+      </el-col>
+      <el-col :span="24" class="interviewMainMod">
+        <el-col class="ModTit">
+          <h4>个人风采</h4>
+        </el-col>
+        <el-col class="ModTxt">
+          <ul>
+            <li></li>
+          </ul>
+        </el-col>
+      </el-col>
+      <el-col :span="24" class="interviewMainMod">
+        <el-col class="ModTit">
+          <h4>自我介绍</h4>
+        </el-col>
+        <el-col class="ModTxt">
+          <ul>
+            <li>{{ info.content || '' }}</li>
+          </ul>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'context',
+  props: {
+    info: { type: Object, default: () => {} },
+  },
+  components: {},
+  data: () => ({
+    pic: require('@/assets/jianlitupian.png'),
+  }),
+  created() {},
+  computed: {},
+  methods: {},
+};
+</script>
+
+<style lang="less" scoped>
+ul {
+  margin: 0;
+  padding: 0;
+}
+li {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+p {
+  margin: 0;
+  padding: 0;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  margin: 0;
+  padding: 0;
+}
+.interviewMainHead {
+  padding: 35px 0 15px 0;
+}
+.headTit {
+  width: 489px;
+  height: 112px;
+}
+.headTit-text {
+  padding: 5px 0;
+}
+.text-inline {
+  margin-right: 10px;
+}
+.headName {
+  font-size: 24px;
+}
+.headImg {
+  float: right;
+  width: 64px;
+  height: 112px;
+  padding: 20px 0 0 0;
+}
+.ModTit {
+  float: left;
+  width: 100px;
+}
+.ModTit h4 {
+  font-size: 18px;
+  font-weight: 500;
+  color: #333;
+  line-height: 27px;
+  margin: 0;
+}
+.ModTxt {
+  width: 541px;
+  border-top: 12px solid #eee;
+  margin: 5px 0 0 0;
+  padding: 10px 0 30px;
+}
+.ModTxt ul {
+  overflow: hidden;
+}
+.interviewMainMod {
+  font-size: 14px;
+  overflow: hidden;
+}
+</style>

+ 199 - 0
src/views/manager/jobs/resume/resume-tem.vue

@@ -0,0 +1,199 @@
+<template>
+  <div id="resume-tem">
+    <el-row ref="search" :gutter="10">
+      <el-col :span="5" class="searchInput"><el-input v-model="search" placeholder="请输入学生名称"></el-input></el-col>
+      <!-- <el-col :span="4" class="searchSelect">
+        <el-select v-model="value" placeholder="全部">
+          <el-option v-for="(item, index) in getOptions()" :key="index" :label="item.label" :value="item.value"> </el-option>
+        </el-select>
+      </el-col> -->
+      <el-col :span="2" class="searchBtn">
+        <el-button circle icon="el-icon-search" @click="toSearch()"></el-button>
+      </el-col>
+    </el-row>
+    <el-row class="pageStyle" ref="resume">
+      <el-col :span="6" class="interview">
+        <ul class="interviewUl">
+          <li v-for="(item, index) in list" :key="index" @click="searchInfo(item.id)">
+            <p class="item-text">{{ item.name || 'none' }}</p>
+          </li>
+          <li v-if="list.length <= 0">
+            <p class="item-text">暂无数据</p>
+          </li>
+        </ul>
+      </el-col>
+      <el-col :span="18" class="p10" v-if="info && info.id">
+        <el-row ref="btnGroup" type="flex" align="middle" justify="center" class="contextRow">
+          <el-col :span="3">
+            <el-button size="small" type="success" @click="changeStatus('2')">暂不考虑</el-button>
+          </el-col>
+          <el-col :span="3">
+            <el-button size="small" type="success" @click="changeStatus('1')">纳入考虑</el-button>
+          </el-col>
+          <el-col :span="3">
+            <el-button size="small" type="success" @click="test()">通&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;知</el-button>
+          </el-col>
+          <!-- <el-col :span="3">
+            <el-button size="small" type="success" @click="test()">简历下载</el-button>
+          </el-col> -->
+        </el-row>
+        <!-- <el-row ref="remarkPart" type="flex" align="middle" justify="center" :gutter="10" class="contextRow">
+          <el-col :span="15">
+            <el-input v-model="input2" placeholder="备注"></el-input>
+          </el-col>
+          <el-col :span="3">
+            <el-button type="success" @click="test()">保存</el-button>
+          </el-col>
+        </el-row> -->
+        <resume-context :info="info"></resume-context>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import resumeContext from './context.vue';
+export default {
+  name: 'resume-tem',
+  props: {
+    list: { type: Array, default: () => [] },
+    info: { type: Object, default: () => {} },
+  },
+  components: {
+    resumeContext,
+  },
+  data: () => ({
+    search: '',
+    input2: '2',
+    value: '',
+    options: [
+      {
+        value: '1',
+        label: '全部',
+      },
+      {
+        value: '2',
+        label: '未查看',
+      },
+      {
+        value: '3',
+        label: '已查看',
+      },
+      {
+        value: '4',
+        label: '不合格',
+      },
+    ],
+    optionsint: [
+      {
+        value: '1',
+        label: '全部',
+      },
+      {
+        value: '2',
+        label: '第一轮',
+      },
+      {
+        value: '3',
+        label: '第二轮',
+      },
+      {
+        value: '4',
+        label: '第三轮以上',
+      },
+    ],
+    optionsemp: [
+      {
+        value: '1',
+        label: '全部',
+      },
+      {
+        value: '2',
+        label: '未明确',
+      },
+      {
+        value: '3',
+        label: '明确录用',
+      },
+    ],
+  }),
+  created() {},
+  computed: {},
+  methods: {
+    toSearch() {
+      this.$emit('search', { studname: this.search });
+    },
+    searchInfo(id) {
+      this.$emit('searchInfo', id);
+    },
+    changeStatus(value) {
+      this.$emit('changeStatus', { id: this.info.id, status: value });
+    },
+    test() {
+      alert(1);
+    },
+    getOptions() {
+      if (this.type === '1') {
+        return this.options;
+      } else if (this.type === '2') {
+        return this.optionsint;
+      } else {
+        return this.optionsemp;
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+ul {
+  margin: 0;
+  padding: 0;
+}
+li {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+p {
+  margin: 0;
+  padding: 0;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  margin: 0;
+  padding: 0;
+}
+.interview {
+  height: 57.4375rem;
+  border-right: 0.0625rem solid #d9dadc;
+  overflow-y: scroll;
+}
+.interviewUl li {
+  border-bottom: 0.0625rem solid #f5f5f5;
+  position: relative;
+  padding: 0.625rem 0.9375rem;
+  background: #44b549;
+}
+.interviewUl li .item-text {
+  color: #fff;
+  font-size: 0.875rem;
+}
+.pageStyle {
+  width: 100%;
+  min-height: 55.4375rem;
+  border: 0.0625rem solid #ccc;
+  margin-top: 1rem;
+}
+.p10 {
+  padding: 0.625rem 1.25rem;
+  overflow-y: scroll;
+}
+.contextRow {
+  margin-top: 1rem;
+}
+</style>

+ 418 - 0
src/views/manager/plan/detail.vue

@@ -0,0 +1,418 @@
+<template>
+  <div id="detail">
+    <detail-layout>
+      <template #title>
+        <back-bar to="/manager/plan/index" title="面试详情"></back-bar>
+      </template>
+      <template #main>
+        <el-row>
+          <ul class="mainInt">
+            <li>
+              <el-col :span="24">
+                <el-radio v-model="radio" label="1">
+                  <p class="danxuan">
+                    <span><i class="el-icon-user-solid"></i>线下面试安排</span>
+                    <br />
+                    <span>自主进行线下面试,提供面试通知。</span>
+                  </p>
+                </el-radio>
+              </el-col>
+            </li>
+            <li>
+              <el-col :span="24">
+                <el-radio v-model="radio" label="2">
+                  <p class="danxuan">
+                    <span><i class="el-icon-video-camera-solid"></i>申请学校免费面试间</span>
+                    <br />
+                    <span>连接学校远程面试间进行视频面试。</span>
+                  </p>
+                </el-radio>
+              </el-col>
+            </li>
+            <li>
+              <el-col :span="24">
+                <el-radio v-model="radio" label="3">
+                  <p class="danxuan">
+                    <span><i class="el-icon-video-camera-solid"></i>申请校园面试间</span>
+                    <br />
+                    <span>连接"同学们好"校园视频面试间进行视频面试。</span>
+                  </p>
+                </el-radio>
+              </el-col>
+            </li>
+          </ul>
+        </el-row>
+        <el-form v-if="radio === '1'" ref="form" :model="form" label-width="80px">
+          <el-col :span="12">
+            <el-form-item label="面试地点">
+              <el-col :span="24">
+                <el-input v-model="form.name"></el-input>
+              </el-col>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="日期">
+              <el-col :span="24">
+                <el-date-picker type="date" placeholder="选择日期" v-model="form.date1" style="width: 100%;"></el-date-picker>
+              </el-col>
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="时间范围">
+              <template>
+                <el-time-picker
+                  is-range
+                  v-model="form.value1"
+                  range-separator="至"
+                  start-placeholder="开始时间"
+                  end-placeholder="结束时间"
+                  placeholder="选择时间范围"
+                >
+                </el-time-picker>
+              </template>
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="说明">
+              <el-input v-model="form.explain"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-col :span="24" class="demandTit">
+              <div class="demandTitBtn">
+                <el-button type="success">导入面试夹</el-button>
+                <el-button type="success">打印面试名单</el-button>
+              </div>
+              <el-row class="profileLi">
+                <template>
+                  <el-table :data="tableData" style="width: 100%" border>
+                    <el-table-column align="center" label="序号" type="index" width="50"> </el-table-column>
+                    <el-table-column align="center" label="岗位" prop="post" width="100"> </el-table-column>
+                    <el-table-column align="center" label="姓名" prop="name" width="100"> </el-table-column>
+                    <el-table-column align="center" label="学校" prop="school" width="100"> </el-table-column>
+                    <el-table-column align="center" label="专业" prop="major" width="110"> </el-table-column>
+                    <el-table-column align="center" label="投递时间" prop="day" width="100"> </el-table-column>
+                    <el-table-column align="center" label="手机号" prop="phone" width="120"> </el-table-column>
+                    <el-table-column align="center" label="操作">
+                      <template slot-scope="scope">
+                        <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
+                        <el-button size="mini" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
+                      </template>
+                    </el-table-column>
+                  </el-table>
+                </template>
+              </el-row>
+            </el-col>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item style="width:100%;margin: 25px 0px;">
+              <el-col :span="3" align="center" style="margin-left:250px;">
+                <el-checkbox v-model="form.checked">群发通知</el-checkbox>
+              </el-col>
+              <el-col :span="3" align="center">
+                <el-button type="success">保存</el-button>
+              </el-col>
+            </el-form-item>
+          </el-col>
+        </el-form>
+        <el-form v-if="radio === '2'" ref="form" :model="form" label-width="80px">
+          <el-col :span="12">
+            <el-form-item label="学校">
+              <el-col :span="24">
+                <el-select v-model="form.school" placeholder="请选择" style="width:100%">
+                  <el-option label="长春大学" value="长春大学"></el-option>
+                  <el-option label="长春光华学院" value="长春光华学院"></el-option>
+                </el-select>
+              </el-col>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="场地">
+              <el-col :span="24">
+                <el-select v-model="form.field" placeholder="请选择" style="width:100%">
+                  <el-option label="教学楼一" value="教学楼一"></el-option>
+                  <el-option label="教学楼二" value="教学楼二"></el-option>
+                </el-select>
+              </el-col>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="活动时间">
+              <el-col :span="24">
+                <el-date-picker type="date" placeholder="选择日期" v-model="form.date1" style="width: 100%;"></el-date-picker>
+              </el-col>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="说明">
+              <el-input v-model="form.explain"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="时间">
+              <el-radio-group v-model="form.time">
+                <el-radio label="1">9:00—12:00</el-radio>
+                <el-radio label="2">14:00—18:00</el-radio>
+                <el-radio label="3">19:00—21:00</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-col :span="24" class="demandTit">
+              <div class="demandTitBtn">
+                <el-button type="success">导入面试夹</el-button>
+                <el-button type="success">打印面试名单</el-button>
+              </div>
+              <el-row class="profileLi">
+                <template>
+                  <el-table :data="tableData" style="width: 100%" border>
+                    <el-table-column align="center" label="序号" type="index" width="50"> </el-table-column>
+                    <el-table-column align="center" label="岗位" prop="post" width="100"> </el-table-column>
+                    <el-table-column align="center" label="姓名" prop="name" width="100"> </el-table-column>
+                    <el-table-column align="center" label="学校" prop="school" width="100"> </el-table-column>
+                    <el-table-column align="center" label="专业" prop="major" width="110"> </el-table-column>
+                    <el-table-column align="center" label="投递时间" prop="day" width="100"> </el-table-column>
+                    <el-table-column align="center" label="手机号" prop="phone" width="120"> </el-table-column>
+                    <el-table-column align="center" label="操作">
+                      <template slot-scope="scope">
+                        <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
+                        <el-button size="mini" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
+                      </template>
+                    </el-table-column>
+                  </el-table>
+                </template>
+              </el-row>
+            </el-col>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item style="width:100%;margin: 25px 0px;">
+              <el-col :span="3" align="center" style="margin-left:250px;">
+                <el-checkbox v-model="form.checked">群发通知</el-checkbox>
+              </el-col>
+              <el-col :span="3" align="center">
+                <el-button type="success">保存</el-button>
+              </el-col>
+            </el-form-item>
+          </el-col>
+        </el-form>
+        <el-form v-if="radio === '3'" ref="form" :model="form" label-width="80px">
+          <el-col :span="12">
+            <el-form-item label="学校">
+              <el-col :span="24">
+                <el-select v-model="form.school" placeholder="请选择" style="width:100%">
+                  <el-option label="请选择" value="请选择"></el-option>
+                  <el-option label="南海测试" value="南海测试"></el-option>
+                  <el-option label="广州商学院" value="广州商学院"></el-option>
+                  <el-option label="广东水利电力职业学院" value="广东水利电力职业学院"></el-option>
+                  <el-option label="回收" value="回收"></el-option>
+                  <el-option label="华南农业大学珠江学院" value="华南农业大学珠江学院"></el-option>
+                  <el-option label="潮汕学院" value="潮汕学院"></el-option>
+                  <el-option label="7楼办公室" value="7楼办公室"></el-option>
+                  <el-option label="广东技术师范学院" value="广东技术师范学院"></el-option>
+                </el-select>
+                <!-- <el-select v-model="form.value" placeholder="请选择">
+                  <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
+                </el-select> -->
+              </el-col>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="时间">
+              <el-col :span="24">
+                <el-date-picker type="date" placeholder="选择日期" v-model="form.date1" style="width: 100%;"></el-date-picker>
+              </el-col>
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="时间">
+              <el-radio-group v-model="form.time">
+                <el-radio label="1">9:00—12:00</el-radio>
+                <el-radio label="2">14:00—18:00</el-radio>
+                <el-radio label="3">19:00—21:00</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="说明">
+              <el-input v-model="form.explain"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-col :span="24" class="demandTit">
+              <div class="demandTitBtn">
+                <el-button type="success">导入面试夹</el-button>
+                <el-button type="success">打印面试名单</el-button>
+              </div>
+              <el-row class="profileLi">
+                <template>
+                  <el-table :data="tableData" style="width: 100%" border>
+                    <el-table-column align="center" label="序号" type="index" width="50"> </el-table-column>
+                    <el-table-column align="center" label="岗位" prop="post" width="100"> </el-table-column>
+                    <el-table-column align="center" label="姓名" prop="name" width="100"> </el-table-column>
+                    <el-table-column align="center" label="学校" prop="school" width="100"> </el-table-column>
+                    <el-table-column align="center" label="专业" prop="major" width="110"> </el-table-column>
+                    <el-table-column align="center" label="投递时间" prop="day" width="100"> </el-table-column>
+                    <el-table-column align="center" label="手机号" prop="phone" width="120"> </el-table-column>
+                    <el-table-column align="center" label="操作">
+                      <template slot-scope="scope">
+                        <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
+                        <el-button size="mini" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
+                      </template>
+                    </el-table-column>
+                  </el-table>
+                </template>
+              </el-row>
+            </el-col>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item style="width:100%;margin: 25px 0px;">
+              <el-col :span="3" align="center" style="margin-left:250px;">
+                <el-checkbox v-model="form.checked">群发通知</el-checkbox>
+              </el-col>
+              <el-col :span="3" align="center">
+                <el-button type="success">保存</el-button>
+              </el-col>
+            </el-form-item>
+          </el-col>
+        </el-form>
+      </template>
+    </detail-layout>
+  </div>
+</template>
+
+<script>
+import detailLayout from '@/layout/detail-layout.vue';
+import backBar from '@/components/back-bar.vue';
+import formItem from '@/components/form-item.vue';
+export default {
+  name: 'detail',
+  props: {},
+  components: {
+    detailLayout,
+    backBar,
+  },
+  data: () => ({
+    radio: '1',
+    form: {
+      name: '',
+      date1: '',
+      explain: '',
+      value1: [new Date(2016, 9, 10, 8, 40), new Date(2016, 9, 10, 9, 40)],
+      checked: false,
+      school: '',
+      field: '',
+      time: '',
+    },
+    tableData: [
+      {
+        post: '前端',
+        name: '顾红伟',
+        school: '长春大学',
+        major: '本科',
+        day: '2019-07-17',
+        phone: '17319450324',
+      },
+    ],
+    // options: [{ value: '长春大学', label: '长春大学' }, { value: '长春光华学院', label: '长春光华学院' }],
+    // value: '',
+  }),
+  created() {},
+  computed: {},
+  methods: {},
+};
+</script>
+
+<style lang="less" scoped>
+.title_space {
+  margin: 0.9rem 0;
+  font-size: 1rem;
+}
+.bord {
+  float: left;
+  background: #f4f5f9;
+  border: 1px solid #e7e8ec;
+}
+.text-owt {
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
+.detailReturn .el-button {
+  float: left;
+  width: 26px;
+  height: 26px;
+  border-radius: 90px;
+  padding: 0;
+  border: 2px solid #9e9fa1;
+  margin: 0 5px 0 0;
+}
+.detailReturn .el-button:hover {
+  background: #fff;
+}
+.detailReturn .el-button:hover i {
+  color: #333333;
+}
+.detailReturn .el-button i {
+  font-size: 15px;
+  font-weight: bold;
+}
+.detailTit {
+  float: left;
+  width: 96%;
+  height: 26px;
+  line-height: 26px;
+  font-size: 14px;
+  color: #333333;
+}
+li {
+  list-style: none;
+}
+p {
+  padding: 0;
+  margin: 0;
+}
+.mainInt {
+  width: 100%;
+  height: 102px;
+  padding: 0 0px;
+}
+.mainInt li {
+  float: left;
+  width: 280px;
+  height: 86px;
+  margin: 0 0 0 40px;
+}
+/deep/.mainInt li .el-radio__input {
+  position: absolute;
+  top: 25%;
+}
+.danxuan {
+  width: 170px;
+  height: 66px;
+  position: relative;
+  top: -15px;
+  left: 20px;
+  padding: 10px;
+  background: #60d295;
+  color: #fff;
+  border-radius: 10px;
+  overflow: hidden;
+}
+.danxuan span {
+  width: 190px;
+  white-space: pre-wrap;
+  line-height: 20px;
+}
+.demandTit {
+  float: left;
+}
+.demandTitBtn {
+  float: right;
+  width: 100%;
+  text-align: right;
+  margin-bottom: 10px;
+}
+.demandTitBtn .el-button {
+  padding: 9px 12px;
+}
+</style>

+ 155 - 0
src/views/manager/plan/index.vue

@@ -0,0 +1,155 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template v-slot:title>
+        <el-row>
+          <el-col :span="24">
+            面试安排
+          </el-col>
+        </el-row>
+      </template>
+      <template v-slot:functionBar>
+        <el-col :span="6">
+          <search v-model="select" type="select" :dataList="testList" placeholder="所有面试类型"></search>
+        </el-col>
+      </template>
+      <template v-slot:addBtn>
+        <el-button type="success" icon="el-icon-plus" @click="$router.push('/manager/plan/detail')">新增面试</el-button>
+      </template>
+
+      <template v-slot:main>
+        <el-table :data="dataList" border :stripe="true">
+          <el-table-column align="center" type="index" label="序号" width="50"></el-table-column>
+          <el-table-column align="center" prop="time" label="面试日时间" width="200"></el-table-column>
+          <el-table-column align="center" prop="person" label="面试人数" width="100"> </el-table-column>
+          <el-table-column align="center" prop="type" label="面试类型" width="200">
+            <template v-slot="scope">
+              <span class="el-icon-s-custom"></span> {{ scope.row.type }}
+            </template>
+          </el-table-column>
+          <el-table-column align="center" prop="status" label="状态" width="100"></el-table-column>
+          <el-table-column align="center" prop="message" label="最近通知"></el-table-column>
+          <el-table-column align="center" label="操作" width="200">
+            <template v-slot="scope">
+              <el-row>
+                <el-col :span="12">
+                  <el-button size="mini" type="text" @click="$router.push('/manager/plan/interview/detail')">查看详情</el-button>
+                </el-col>
+                <el-col :span="12">
+                  <el-button size="mini" type="text" v-popover:pop>通知学生</el-button>
+                  <el-popover ref="pop" placement="bottom" width="250" trigger="click" @show="toSendMsg(scope.row)">
+                    <el-row style="padding-top: 1rem;">
+                      <el-col>
+                        <span>请输入信息</span>
+                      </el-col>
+                      <el-col>
+                        <el-input type="textarea" :rows="10" placeholder="请输入信息"></el-input>
+                      </el-col>
+                    </el-row>
+                    <el-row type="flex" justify="center" style="padding-top: 1rem;">
+                      <el-button type="success">确认</el-button>
+                      <el-button>取消</el-button>
+                    </el-row>
+                  </el-popover>
+                </el-col>
+                <el-col :span="12">
+                  <el-button size="mini" type="text" @click="$router.push('/manager/plan/notify/record')">通知记录 </el-button>
+                </el-col>
+              </el-row>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <el-row class="row_pagination">
+          <el-col :span="24">
+            <el-pagination
+              @current-change="handleCurrentChange"
+              :current-page="currentPage"
+              :page-size="15"
+              layout="total, prev, pager, next, jumper"
+              :total="totalRow"
+            >
+            </el-pagination>
+          </el-col>
+        </el-row>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import search from '@/components/search.vue';
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  components: {
+    listNormal,
+    search,
+  },
+  data: () => ({
+    testList: [
+      { label: '所有面试类型', value: 1 },
+      { label: '线下面试安排', value: 2 },
+      { 学校视频面试: '社招', value: 3 },
+      { label: '校园面试', value: 4 },
+      { label: '云面试', value: 5 },
+    ],
+    circleUrl: '',
+    dataList: [
+      {
+        time: '2019年7月2日10:00-11:00',
+        person: '1人',
+        type: '线下面试安排',
+        status: '已过期',
+        message: ' ',
+      },
+    ],
+    select: '',
+    listSelect: [],
+    currentPage: 1,
+    totalRow: 60,
+  }),
+  created() {
+    // this.search();
+  },
+  methods: {
+    ...mapActions(['getList']),
+    test() {
+      console.log('in function:');
+    },
+    async search(val) {
+      let skip = 0;
+      if (val) {
+        this.currentPage = val;
+        skip = (this.currentPage - 1) * this.$limit;
+      }
+      let result = await this.getList();
+      this.$set(this, `dataList`, result);
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.search();
+    },
+    toSendMsg(val) {
+      console.log(val);
+    },
+    handleDelete(val) {
+      console.log(val);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.icons {
+  zoom: 2;
+  position: relative;
+  top: 0.5rem;
+}
+</style>

+ 100 - 0
src/views/manager/plan/interview-detail.vue

@@ -0,0 +1,100 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template #title>
+        <back-bar to="/manager/plan/index" title="面试详情"></back-bar>
+      </template>
+      <template #main>
+        <el-row>
+          <el-col :span="6" class="l-top">
+            <el-col>
+              <span>日期:2019年7月2日</span>
+            </el-col>
+            <el-col :span="20">
+              <span>开始:10:00</span>
+              <span>&nbsp;&nbsp;&nbsp;&nbsp;</span>
+              <span>截止:11:00</span>
+            </el-col>
+            <el-col align="center" style="background-color: #cccccc;">
+              <span>已结束</span>
+            </el-col>
+            <el-col :span="24">
+              <el-tabs v-model="activeName" :stretch="true">
+                <el-tab-pane label="待面0" name="first"></el-tab-pane>
+                <el-tab-pane label="已面1" name="second">
+                  <el-row>
+                    <el-col :span="4" class="f_tit"><span>1</span></el-col>
+                    <el-col :span="20">
+                      <el-col :span="14" class="spa">
+                        <el-link type="primary">
+                          <span>111</span>
+                        </el-link>
+                      </el-col>
+                      <el-col :span="10">
+                        <el-button type="success" style="padding: 3px 15px;"><span>录用</span></el-button>
+                      </el-col>
+                      <el-col class="spa"><span>云就业</span></el-col>
+                      <el-col class="spa"><span>本科生毕业&nbsp;交通运输</span></el-col>
+                      <el-col class="spa"><span>岗位:eeee</span></el-col>
+                    </el-col>
+                  </el-row>
+                </el-tab-pane>
+              </el-tabs>
+            </el-col>
+          </el-col>
+          <el-col :span="18" class="l-top" style="padding:14px;">
+            <resume-context v-if="info.id" :info="info"></resume-context>
+            <span v-else>暂无简历数据</span>
+          </el-col>
+        </el-row>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import resumeContext from '@/views/manager/jobs/resume/context.vue';
+import listNormal from '@/layout/list-normal.vue';
+import backBar from '@/components/back-bar.vue';
+export default {
+  components: {
+    listNormal,
+    backBar,
+    resumeContext,
+  },
+  data: () => ({
+    activeName: 'detail',
+    info: { id: 1 },
+  }),
+  methods: {},
+};
+</script>
+
+<style lang="less" scoped>
+.l-top {
+  height: -webkit-fill-available;
+  padding-top: 14px;
+  border-left-style: solid;
+  border-right-style: solid;
+  border-top-style: solid;
+  border-width: 1px;
+  border-color: #c0c4cc;
+}
+/deep/.spa {
+  padding: 2px 5px;
+}
+/deep/.el-tabs__item.is-active {
+  color: #44b549;
+}
+/deep/.el-tabs__active-bar {
+  background-color: #44b549;
+}
+.f_tit {
+  padding: 40px 0;
+  background-color: #f3f3f3;
+  text-indent: 15px;
+}
+/deep/.el-tabs__item:hover {
+  color: #000;
+}
+</style>

+ 96 - 0
src/views/manager/plan/notify-record.vue

@@ -0,0 +1,96 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template v-slot:title>
+        <el-row>
+          <el-col :span="1"><el-button icon="el-icon-arrow-left" size="mini" circle @click="$router.push('/manager/plan/index')"></el-button> </el-col>
+          <el-col style="margin-top:0.5rem" :span="23">
+            通知记录
+          </el-col>
+        </el-row>
+      </template>
+      <template v-slot:functionBar>
+        <search v-model="select" type="select" :dataList="testList" placeholder="查阅状态" style="width:30%"></search>
+      </template>
+
+      <template v-slot:main>
+        <el-table :data="dataList" border :stripe="true">
+          <el-table-column align="center" type="index" label="序号" width="50"></el-table-column>
+          <el-table-column align="center" prop="time" label="面试时间" width="200"></el-table-column>
+          <el-table-column align="center" prop="school" label="学校名称" width="150"> </el-table-column>
+          <el-table-column align="center" prop="name" label="姓名" width="150"> </el-table-column>
+          <el-table-column align="center" prop="date" label="接受内容"></el-table-column>
+          <el-table-column align="center" prop="status" label="是否查阅" width="100"></el-table-column>
+          <el-table-column align="center" prop="sendtime" label="发送时间" width="200"> </el-table-column>
+        </el-table>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import search from '@/components/search.vue';
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  components: {
+    listNormal,
+    search,
+  },
+  data: () => ({
+    testList: [{ label: '查阅状态', value: 1 }, { label: '已查阅', value: 2 }, { label: '未查阅', value: 3 }],
+    dataList: [
+      {
+        time: '2019年7月2日10:00-11:00',
+        person: '1人',
+        school: '云就业',
+        name: '刘睿峰',
+        date: '1',
+        status: '否',
+        sendtime: '2019-07-22 16:16 ',
+      },
+    ],
+    select: '',
+    listSelect: [],
+    currentPage: 1,
+    totalRow: 60,
+  }),
+  created() {
+    // this.search();
+  },
+  methods: {
+    ...mapActions(['getList']),
+    test() {
+      console.log('in function:');
+    },
+    async search() {
+      let result = await this.getList();
+      this.$set(this, `dataList`, result);
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.search();
+    },
+    toSendMsg(val) {
+      console.log(val);
+    },
+    handleDelete(val) {
+      console.log(val);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.icons {
+  zoom: 2;
+  position: relative;
+  top: 0.5rem;
+}
+</style>

+ 143 - 0
src/views/manager/record/index.vue

@@ -0,0 +1,143 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template v-slot:title>
+        <!-- 此处v-slot:title后可以加="data",data代表将slot传来的参数再次封装后的key=>{data:params}(data可以随意替换),
+        也就是说,想取到最后的数据集,我们需要解开1层object,(template声明的名称).(slot :xxx绑定名称的 xxx)才能取到数据 -->
+        <!-- 当然,我们现在没涉及用上述功能,只是就想搞明白而已╮(╯▽╰)╭ -->
+        <!-- 如果组件文件夹下的needOrNot文件夹的组件可以使用,那么这部分就应该需要理解了,因为操作按钮部分是需要自己写 -->
+        邀约记录
+      </template>
+      <template v-slot:functionBar>
+        <el-col :span="5">
+          <search v-model="select" type="select" placeholder="邀约状态"></search>
+        </el-col>
+        <el-col :span="6">
+          <search v-model="input" placeholder="请输入姓名或专业名称"></search>
+        </el-col>
+        <el-col :span="2">
+          <el-button type="info" circle icon="el-icon-search icons"></el-button>
+        </el-col>
+      </template>
+      <template v-slot:main>
+        <el-table :data="dataList" stripe border style="width: 100%">
+          <el-table-column type="index" align="center" prop="num" label="序号" width="50"> </el-table-column>
+          <el-table-column align="center" prop="picture" label="头像">
+            <div class="demo-basic--circle">
+              <div class="block"><el-avatar :size="80" :src="circleUrl"></el-avatar></div>
+            </div>
+          </el-table-column>
+          <el-table-column prop="date" label="基本信息">
+            <el-row>
+              <el-col :span="24">
+                <!-- 链接简历模块 -->
+                <el-button type="text">姓名</el-button>
+              </el-col>
+              <el-col :span="24">
+                性别
+              </el-col>
+              <el-col :span="24">
+                毕业学校
+              </el-col>
+            </el-row>
+          </el-table-column>
+          <el-table-column prop="name" label="邀约职位">
+            <!-- 链接到职位详情 -->
+            <el-row><el-button type="text">职业</el-button></el-row>
+          </el-table-column>
+          <el-table-column prop="date" align="center" label="邀约时间"></el-table-column>
+          <el-table-column prop="date" align="center" label="状态">
+            <template v-slot="scope">
+              已忽略
+            </template>
+          </el-table-column>
+          <el-table-column align="center" prop="address" label="操作">
+            <template slot-scope="scope">
+              <el-button @click="handleClick(scope.row)" type="text" size="small">查看简历</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <el-row class="row_pagination">
+          <el-col :span="24">
+            <el-pagination
+              @current-change="handleCurrentChange"
+              :current-page="currentPage"
+              :page-size="15"
+              layout="total, prev, pager, next, jumper"
+              :total="totalRow"
+            >
+            </el-pagination>
+          </el-col>
+        </el-row>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import search from '@/components/search.vue';
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  components: {
+    listNormal,
+    search,
+  },
+  data: () => ({
+    dataList: [],
+    select: '',
+    circleUrl: '',
+    sizeList: '',
+    input: '',
+    listSelect: [],
+    currentPage: 1,
+    totalRow: 60,
+  }),
+  created() {
+    this.search();
+  },
+  methods: {
+    ...mapActions(['getList']),
+    test() {
+      console.log('in function:');
+    },
+    async search(val) {
+      let skip = 0;
+      if (val) {
+        this.currentPage = val;
+        skip = (this.currentPage - 1) * this.$limit;
+      }
+      let result = await this.getList();
+      this.$set(this, `dataList`, result);
+    },
+    selectInfo(selection) {
+      //选择/全选方法,删除或者其他批量操作使用
+      let ids = [];
+      selection.map(item => {
+        ids.push(item.id);
+      });
+      this.$set(this, `listSelect`, ids);
+      console.log(ids);
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.search();
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.icons {
+  zoom: 2;
+  position: relative;
+  top: 0.5rem;
+}
+</style>

+ 86 - 0
src/views/manager/resume/index.vue

@@ -0,0 +1,86 @@
+<template>
+  <div id="index">
+    <list-tab :tab="tabsTitle">
+      <template v-slot:title>
+        简历筛选
+      </template>
+      <template v-slot:main1>
+        <list type="0" :info="info" :dataList="dataList0" :totalRow="totalRow1" @changePage="search" @selectInfo="selectInfo" @searchInfo="searchInfo"></list>
+      </template>
+      <template v-slot:main2>
+        <list type="1" :info="info" :dataList="dataList1" :totalRow="totalRow2" @changePage="search" @selectInfo="selectInfo" @searchInfo="searchInfo"></list>
+      </template>
+      <template v-slot:main3>
+        <list type="2" :info="info" :dataList="dataList2" :totalRow="totalRow2" @changePage="search" @selectInfo="selectInfo" @searchInfo="searchInfo"></list>
+      </template>
+    </list-tab>
+  </div>
+</template>
+
+<script>
+import list from './list.vue';
+import listTab from '@/layout/list-tab.vue';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'rindex',
+  props: {},
+  components: {
+    listTab,
+    list,
+  },
+  data: () => ({
+    testList: [{ label: 'test', value: 1 }, { label: 'name', value: 2 }, { label: 'lock', value: 3 }, { label: 'not', value: 4 }],
+    tabsTitle: ['未处理', '纳入考虑', '暂不考虑'], //简历库
+    info: {},
+    dataList0: [],
+    totalRow0: 20,
+    dataList1: [],
+    totalRow1: 10,
+    dataList2: [],
+    totalRow2: 50,
+  }),
+  created() {
+    this.search({ type: '0' });
+    this.search({ type: '1' });
+    this.search({ type: '2' });
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['lettersOperation']),
+    async search({ type, currentPage, searchInfo }) {
+      let skip = 0;
+      if (currentPage) {
+        skip = (currentPage - 1) * this.$limit;
+      }
+      //1直接拿着参数发送请求
+      let result = await this.lettersOperation({
+        type: 'list',
+        data: { status: type, skip: skip, limit: this.$limit, corpname: this.user.corpname, ...searchInfo },
+      });
+      if (`${result.errcode}` === '0') {
+        //给this=>vue的实例下在中的list属性,赋予result。data的值
+        this.$set(this, `dataList${type}`, result.data);
+        this.$set(this, `totalRow${type}`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    async searchInfo(id) {
+      let result = await this.lettersOperation({ type: 'search', data: { id: id } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `info`, result.data);
+      }
+    },
+    selectInfo({ type, ids }) {
+      //type=>哪个标签页
+      //ids,选择的id
+      console.log(type, ids);
+      //缓存在本组件中(this.$set在本组件中),为之后的批量操作使用
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 103 - 0
src/views/manager/resume/list.vue

@@ -0,0 +1,103 @@
+<template>
+  <div id="list">
+    <el-row :gutter="20" style="padding-bottom:1.2rem;">
+      <el-col :span="6">
+        <el-input v-model="searchInfo.studname" placeholder="请输入学生名称"></el-input>
+      </el-col>
+      <el-col :span="2">
+        <el-button type="info" circle icon="el-icon-search icons" @click="handleCurrentChange"></el-button>
+      </el-col>
+    </el-row>
+    <el-table size="small" :data="list" stripe border style="width: 100%">
+      <el-table-column align="center" type="index" prop="num" label="序号" width="50"></el-table-column>
+      <el-table-column align="center" prop="name" label="姓名"></el-table-column>
+      <el-table-column align="center" prop="skill" label="专业技能"></el-table-column>
+      <el-table-column align="center" width="130" prop="data" label="操作">
+        <template v-slot="scoped">
+          <el-row>
+            <el-col :span="12">
+              <!-- 链接到查看简历公共模块 -->
+              <el-button size="mini" type="text" @click="checkResume(scoped.row.id)">查看简历</el-button>
+            </el-col>
+            <el-col :span="12" v-if="type !== '2'">
+              <el-button size="mini" type="text">纳入考虑</el-button>
+            </el-col>
+            <el-col :span="12" v-if="type !== '3'">
+              <el-button size="mini" type="text">暂不考虑</el-button>
+            </el-col>
+          </el-row>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-row class="row_pagination">
+      <el-col :span="24">
+        <el-pagination
+          @current-change="handleCurrentChange"
+          :current-page="currentPage"
+          :page-size="$limit"
+          layout="total, prev, pager, next, jumper"
+          :total="totalRow"
+        >
+        </el-pagination>
+      </el-col>
+    </el-row>
+    <el-dialog title="简历详情" center :visible.sync="resumeDialog" :fullscreen="true">
+      <resumes :info="info"></resumes>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import resumes from '@resume/src/views/resume.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'list',
+  props: {
+    type: { type: String, defalut: 'none' },
+    dataList: { type: Array, defalut: () => [] },
+    totalRow: { type: Number, defalut: 0 },
+    info: { type: Object, default: () => {} },
+  },
+  components: {
+    resumes,
+  },
+  data: () => ({
+    currentPage: 1,
+    searchInfo: {},
+    resumeDialog: false,
+    list: [],
+    circleUrl: '',
+  }),
+  created() {},
+  computed: {},
+  methods: {
+    selectInfo(selection) {
+      //选择/全选方法,删除或者其他批量操作使用
+      let ids = [];
+      selection.map(item => {
+        ids.push(item.id);
+      });
+      this.$emit('selectInfo', { type: this.type, ids: ids });
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val ? 1 : val;
+      this.$emit('changePage', { type: this.type, currentPage: this.currentPage, searchInfo: this.searchInfo });
+    },
+    checkResumecheckResume(id) {
+      this.$emit('searchInfo', id);
+      this.resumeDialog = true;
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+/deep/.el-dialog__body {
+  padding: 0;
+}
+</style>

+ 42 - 0
src/views/test/test-detail.vue

@@ -0,0 +1,42 @@
+<template>
+  <div id="test-detail">
+    <detail-layout>
+      <template #title>
+        <el-button circle size="mini" class="el-icon-arrow-left" style="margin-right: 0.5rem;"></el-button>详情测试
+      </template>
+      <template #main>
+        <el-form :data="info" :rules="rules" ref="form">
+          <el-row>
+            <form-item v-model="info.value3" label="注册资金" :disabled="false" prop="value3" placeholder="请输入注册资金"></form-item>
+          </el-row>
+        </el-form>
+      </template>
+    </detail-layout>
+  </div>
+</template>
+
+<script>
+import formItem from '@/components/form-item.vue';
+import detailLayout from '@/layout/detail-layout.vue';
+export default {
+  name: 'test-detail',
+  props: {},
+  components: {
+    detailLayout,
+    formItem,
+  },
+  data: () => ({
+    info: {},
+  }),
+  created() {},
+  computed: {},
+  methods: {},
+};
+</script>
+
+<style lang="less" scoped>
+.title_space {
+  margin: 0.9rem 0;
+  font-size: 1rem;
+}
+</style>

+ 142 - 0
src/views/test/test-list-normal.vue

@@ -0,0 +1,142 @@
+<template>
+  <div id="home">
+    <list-normal>
+      <template v-slot:title>
+        <!-- 此处v-slot:title后可以加="data",data代表将slot传来的参数再次封装后的key=>{data:params}(data可以随意替换),
+        也就是说,想取到最后的数据集,我们需要解开1层object,(template声明的名称).(slot :xxx绑定名称的 xxx)才能取到数据 -->
+        <!-- 当然,我们现在没涉及用上述功能,只是就想搞明白而已╮(╯▽╰)╭ -->
+        <!-- 如果组件文件夹下的needOrNot文件夹的组件可以使用,那么这部分就应该需要理解了,因为操作按钮部分是需要自己写 -->
+        测试普通列表标题
+      </template>
+      <template v-slot:functionBar>
+        <el-col :span="6">
+          <search v-model="select" type="select" :dataList="testList"></search>
+        </el-col>
+        <el-col :span="6">
+          <search v-model="select"></search>
+        </el-col>
+        <el-col :span="2">
+          <el-button type="info" circle icon="el-icon-search icons"></el-button>
+        </el-col>
+      </template>
+      <template v-slot:addBtn>
+        <el-button type="success" icon="el-icon-plus">新增</el-button>
+      </template>
+      <template v-slot:batch>
+        <el-row type="flex" justify="end">
+          <el-col :span="4"></el-col>
+          <el-col :span="3">
+            <el-dropdown>
+              <el-button size="small" style="width:6.5rem" type="success" plain icon="el-icon-s-check">批量审核</el-button>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item>审核成功</el-dropdown-item>
+                <el-dropdown-item>审核失败</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+            <!-- <el-button style="width:100%" size="small" type="success" plain icon="el-icon-s-check">批量审核</el-button> -->
+          </el-col>
+          <el-col :span="3">
+            <el-dropdown>
+              <el-button style="width:6.5rem" size="small" type="primary" plain icon="el-icon-document-copy" @click="test">批量导出</el-button>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item>审核成功</el-dropdown-item>
+                <el-dropdown-item>审核失败</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </el-col>
+          <el-col :span="3"><el-button style="width:6.5rem" size="small" type="info" plain icon="el-icon-refresh">同步数据</el-button></el-col>
+          <el-col :span="3"><el-button style="width:6.5rem" size="small" type="warning" plain icon="el-icon-printer">批量打印</el-button></el-col>
+          <el-col :span="3"><el-button style="width:6.5rem" size="small" type="danger" plain icon="el-icon-delete">批量删除</el-button></el-col>
+        </el-row>
+      </template>
+
+      <template v-slot:main>
+        <el-table :data="dataList" :stripe="true" :border="true" @select="selectInfo" @select-all="selectInfo">
+          <el-table-column align="center" type="selection" width="40"> </el-table-column>
+          <el-table-column align="center" label="姓名" sortable prop="name"></el-table-column>
+          <el-table-column align="center" label="年龄" sortable prop="age"></el-table-column>
+          <el-table-column align="center" label="电话" prop="tel"></el-table-column>
+          <el-table-column align="center" label="操作">
+            <template v-slot="scope">
+              <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
+              <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <el-row class="row_pagination">
+          <el-col :span="24">
+            <el-pagination
+              @current-change="handleCurrentChange"
+              :current-page="currentPage"
+              :page-size="15"
+              layout="total, prev, pager, next, jumper"
+              :total="totalRow"
+            >
+            </el-pagination>
+          </el-col>
+        </el-row>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import search from '@/components/search.vue';
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'home',
+  components: {
+    listNormal,
+    search,
+  },
+  data: () => ({
+    testList: [{ label: 'test', value: 1 }, { label: 'name', value: 2 }, { label: 'lock', value: 3 }, { label: 'not', value: 4 }],
+    dataList: [],
+    select: '',
+    listSelect: [],
+    currentPage: 1,
+    totalRow: 60,
+  }),
+  created() {
+    this.search();
+  },
+  methods: {
+    ...mapActions(['getList']),
+    test() {
+      console.log('in function:');
+    },
+    async search() {
+      let result = await this.getList();
+      this.$set(this, `dataList`, result);
+    },
+    selectInfo(selection) {
+      //选择/全选方法,删除或者其他批量操作使用
+      let ids = [];
+      selection.map(item => {
+        ids.push(item.id);
+      });
+      this.$set(this, `listSelect`, ids);
+      console.log(ids);
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.search();
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.icons {
+  zoom: 2;
+  position: relative;
+  top: 0.5rem;
+}
+</style>

+ 92 - 0
src/views/test/test-list-tab.vue

@@ -0,0 +1,92 @@
+<template>
+  <div id="test-list-tab">
+    <list-tab :tab="tabsTitle">
+      <template v-slot:title>
+        测试标签页框架标题
+      </template>
+      <template v-slot:functionBar>
+        <el-col :span="6">
+          <search v-model="select" type="select" :dataList="testList"></search>
+        </el-col>
+        <el-col :span="6">
+          <search v-model="select"></search>
+        </el-col>
+        <el-col :span="2">
+          <el-button type="info" circle icon="el-icon-search icons"></el-button>
+        </el-col>
+      </template>
+      <template v-slot:addBtn>
+        <el-button type="success" icon="el-icon-plus">新增</el-button>
+      </template>
+      <template v-slot:main1>
+        <list type="1" :dataList="dataList1" :totalRow="totalRow1" @changePage="changePage" @selectInfo="selectInfo"></list>
+      </template>
+      <template v-slot:main2>
+        <list type="2" :dataList="dataList2" :totalRow="totalRow2" @changePage="changePage" @selectInfo="selectInfo"></list>
+      </template>
+      <template v-slot:main3>
+        <list type="3" :dataList="dataList3" :totalRow="totalRow3" @changePage="changePage" @selectInfo="selectInfo"></list>
+      </template>
+    </list-tab>
+  </div>
+</template>
+
+<script>
+import search from '@/components/search.vue';
+import list from './test-list-tab/list.vue';
+import listTab from '@/layout/list-tab.vue';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'test-list-tab',
+  props: {},
+  components: {
+    listTab,
+    list,
+    search,
+  },
+  data: () => ({
+    testList: [{ label: 'test', value: 1 }, { label: 'name', value: 2 }, { label: 'lock', value: 3 }, { label: 'not', value: 4 }],
+    tabsTitle: ['标签页1', '标签页2', '标签页3'],
+    select: '',
+    dataList1: [],
+    totalRow1: 20,
+    pageIds1: [],
+    dataList2: [],
+    totalRow2: 50,
+    pageIds2: [],
+    dataList3: [],
+    totalRow3: 10,
+    pageIds3: [],
+  }),
+  created() {
+    this.search();
+  },
+  computed: {},
+  methods: {
+    ...mapActions(['getList']),
+    test(data) {
+      console.log(data);
+    },
+    async search() {
+      let result = await this.getList();
+      this.$set(this, `dataList1`, result);
+      this.$set(this, `dataList2`, result);
+      this.$set(this, `dataList3`, result);
+    },
+    changePage({ type, currentPage }) {
+      //type=>哪个标签页
+      //currentPage=>标签页的当前页,用来计算skip的
+      console.log(type, currentPage);
+      //根据type查询对应的数据
+    },
+    selectInfo({ type, ids }) {
+      //type=>哪个标签页
+      //ids,选择的id
+      console.log(type, ids);
+      //缓存在本组件中(this.$set在本组件中),为之后的批量操作使用
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 101 - 0
src/views/test/test-list-tab/list.vue

@@ -0,0 +1,101 @@
+<template>
+  <div id="list">
+    <list-normal>
+      <template v-slot:batch>
+        <el-row type="flex" justify="end">
+          <el-col :span="4"></el-col>
+          <el-col :span="3">
+            <el-dropdown>
+              <el-button size="small" style="width:6.5rem" type="success" plain icon="el-icon-s-check">批量审核</el-button>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item>审核成功</el-dropdown-item>
+                <el-dropdown-item>审核失败</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+            <!-- <el-button style="width:100%" size="small" type="success" plain icon="el-icon-s-check">批量审核</el-button> -->
+          </el-col>
+          <el-col :span="3">
+            <el-dropdown>
+              <el-button style="width:6.5rem" size="small" type="primary" plain icon="el-icon-document-copy" @click="test">批量导出</el-button>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item>审核成功</el-dropdown-item>
+                <el-dropdown-item>审核失败</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </el-col>
+          <el-col :span="3"><el-button style="width:6.5rem" size="small" type="info" plain icon="el-icon-refresh">同步数据</el-button></el-col>
+          <el-col :span="3"><el-button style="width:6.5rem" size="small" type="warning" plain icon="el-icon-printer">批量打印</el-button></el-col>
+          <el-col :span="3"><el-button style="width:6.5rem" size="small" type="danger" plain icon="el-icon-delete">批量删除</el-button></el-col>
+        </el-row>
+      </template>
+      <template v-slot:main>
+        <el-table :data="dataList" :stripe="true" :border="true" @select="selectInfo" @select-all="selectInfo">
+          <el-table-column align="center" type="selection" width="40"> </el-table-column>
+          <el-table-column align="center" label="姓名" sortable prop="name"></el-table-column>
+          <el-table-column align="center" label="年龄" sortable prop="age"></el-table-column>
+          <el-table-column align="center" label="电话" prop="tel"></el-table-column>
+          <el-table-column align="center" label="操作">
+            <template v-slot="scope">
+              <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
+              <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <el-row class="row_pagination">
+          <el-col :span="24">
+            <el-pagination
+              @current-change="handleCurrentChange"
+              :current-page="currentPage"
+              :page-size="15"
+              layout="total, prev, pager, next, jumper"
+              :total="totalRow"
+            >
+            </el-pagination>
+          </el-col>
+        </el-row>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import listNormal from '@/layout/list-normal.vue';
+export default {
+  name: 'list',
+  props: {
+    type: { type: String, defalut: 'none' },
+    dataList: { type: Array, defalut: () => [] },
+    totalRow: { type: Number, defalut: 0 },
+  },
+  components: {
+    listNormal,
+  },
+  data: () => ({
+    currentPage: 1,
+  }),
+  created() {},
+  computed: {},
+  methods: {
+    selectInfo(selection) {
+      //选择/全选方法,删除或者其他批量操作使用
+      let ids = [];
+      selection.map(item => {
+        ids.push(item.id);
+      });
+      this.$emit('selectInfo', { type: this.type, ids: ids });
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.$emit('changePage', { type: this.type, currentPage: this.currentPage });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+</style>

+ 43 - 0
vue.config.js

@@ -0,0 +1,43 @@
+const path = require('path');
+const resumeSrc = path.resolve(__dirname, '../web-common');
+module.exports = {
+  publicPath: '/',
+  configureWebpack: config => {
+    Object.assign(config, {
+      // 开发生产共同配置
+      resolve: {
+        alias: {
+          '@': path.resolve(__dirname, './src'),
+          '@c': path.resolve(__dirname, './src/components'),
+          '@a': path.resolve(__dirname, './src/assets'),
+          '@resume': resumeSrc,
+        },
+      },
+    });
+  },
+  devServer: {
+    port: '8003',
+    //api地址前缀
+    proxy: {
+      '/api': {
+        target: 'http://smart.cc-lotus.info',
+        changeOrigin: true,
+        ws: true,
+      },
+      '/files': {
+        target: 'http://smart.cc-lotus.info',
+        changeOrigin: true,
+        ws: true,
+      },
+      '/ws': {
+        target: 'http://smart.cc-lotus.info',
+        ws: true,
+      },
+      '/weixin': {
+        target: 'http://smart.cc-lotus.info',
+        changeOrigin: true,
+        ws: true,
+      },
+    },
+  },
+};