lrf402788946 5 سال پیش
کامیت
9531fc5b91
61فایلهای تغییر یافته به همراه6005 افزوده شده و 0 حذف شده
  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. 58 0
      package.json
  7. BIN
      public/favicon.ico
  8. 17 0
      public/index.html
  9. 18 0
      src/App.vue
  10. BIN
      src/assets/default_logo.png
  11. BIN
      src/assets/logo.png
  12. 81 0
      src/components/bind.vue
  13. 34 0
      src/components/form-item.vue
  14. 263 0
      src/components/new-upload.vue
  15. 54 0
      src/components/qrcode.vue
  16. 240 0
      src/components/tag-all-select.vue
  17. 148 0
      src/components/tag-checkbox.vue
  18. 149 0
      src/components/upload-file.vue
  19. 75 0
      src/components/upload.vue
  20. 35 0
      src/layout/detail-layout.vue
  21. 212 0
      src/layout/layout-part/heads.vue
  22. 46 0
      src/layout/layout-part/menus.vue
  23. 57 0
      src/layout/list-normal.vue
  24. 55 0
      src/layout/list-tab.vue
  25. 75 0
      src/layout/main-layout.vue
  26. 23 0
      src/main.js
  27. 19 0
      src/plugins/axios.js
  28. 39 0
      src/plugins/check-res.js
  29. 5 0
      src/plugins/element.js
  30. 6 0
      src/plugins/filters.js
  31. 27 0
      src/plugins/loading.js
  32. 4 0
      src/plugins/meta.js
  33. 38 0
      src/plugins/methods.js
  34. 20 0
      src/plugins/setting.js
  35. 65 0
      src/plugins/stomp.js
  36. 10 0
      src/plugins/var.js
  37. 47 0
      src/router.js
  38. 198 0
      src/store.js
  39. 132 0
      src/util/axios-wrapper.js
  40. 10 0
      src/util/filters.js
  41. 50 0
      src/util/methods-util.js
  42. 47 0
      src/util/optionTitles.js
  43. 96 0
      src/util/qrcode.vue
  44. 69 0
      src/util/user-util.js
  45. 261 0
      src/views/index.vue
  46. 183 0
      src/views/practice/index.vue
  47. 450 0
      src/views/resume/index.vue
  48. 139 0
      src/views/want/info/context/in-jobfair.vue
  49. 147 0
      src/views/want/info/context/in-talk.vue
  50. 110 0
      src/views/want/info/context/internship.vue
  51. 123 0
      src/views/want/info/context/jobinfo.vue
  52. 122 0
      src/views/want/info/context/jobs.vue
  53. 136 0
      src/views/want/info/context/out-jobfair.vue
  54. 141 0
      src/views/want/info/context/out-talk.vue
  55. 121 0
      src/views/want/info/index.vue
  56. 191 0
      src/views/want/interview/detail.vue
  57. 107 0
      src/views/want/interview/index.vue
  58. 106 0
      src/views/want/invite/index.vue
  59. 183 0
      src/views/want/practice/index.vue
  60. 807 0
      src/views/want/resume/index.vue
  61. 68 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-student
+
+## 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"]
+};

+ 58 - 0
package.json

@@ -0,0 +1,58 @@
+{
+  "name": "web-student",
+  "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",
+    "naf-core": "^0.1.2",
+    "qrcode": "^1.4.1",
+    "vue": "^2.6.10",
+    "vue-meta": "^2.2.0",
+    "vue-router": "^3.0.3",
+    "vuex": "^3.0.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-student</title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but web-student 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>

+ 18 - 0
src/App.vue

@@ -0,0 +1,18 @@
+<template>
+  <div id="app">
+    <main-layout></main-layout>
+  </div>
+</template>
+<script>
+import mainLayout from '@/layout/main-layout.vue';
+export default {
+  name: 'app',
+  components: {
+    mainLayout,
+  },
+  created() {},
+  methods: {},
+};
+</script>
+
+<style lang="less"></style>

BIN
src/assets/default_logo.png


BIN
src/assets/logo.png


+ 81 - 0
src/components/bind.vue

@@ -0,0 +1,81 @@
+<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 = { userid: this.user.id, wxtoken: wxtoken };
+      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>

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

@@ -0,0 +1,34 @@
+<template>
+  <div id="form-item">
+    <el-row class="input_row">
+      <el-form-item :prop="prop">
+        <el-col :span="4">{{ label }}</el-col>
+        <el-col :span="20">
+          <slot></slot>
+        </el-col>
+      </el-form-item>
+    </el-row>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'form-item',
+  props: {
+    label: { type: String, default: '' },
+    prop: { type: String, default: '' },
+  },
+  components: {},
+  data: () => ({}),
+  created() {},
+  computed: {},
+  methods: {},
+};
+</script>
+
+<style lang="less" scoped>
+.input_row {
+  padding: 0 3rem;
+  margin-top: 1rem;
+}
+</style>

+ 263 - 0
src/components/new-upload.vue

@@ -0,0 +1,263 @@
+<template>
+  <div class="img-list">
+    <div class="img-li-box" v-for="(item, key) in imgList" :key="key">
+      <img class="img-li-b--url" :src="item.url" />
+      <div class="img-li-b--bottom">
+        <div class="img-li-b--name">{{ item.name }}</div>
+        <el-button type="text" @click="handleFileName(item, key)">修改名字</el-button>
+      </div>
+
+      <!-- 删除icon -->
+      <div class="img-li-b--delete">
+        <i @click="handleFileRemove(item, key)" class="el-icon-delete"></i>
+      </div>
+
+      <!-- 放大icon -->
+      <div class="img-li-b--layer" @click="handleFileEnlarge(item.url)">
+        <i class="el-icon-view"></i>
+      </div>
+    </div>
+
+    <!-- 上传进度 -->
+    <div v-if="!pass && progress !== 0" class="img-li-box img-li-b--progress">
+      <el-progress type="circle" :percentage="progress" :status="proStatus"></el-progress>
+    </div>
+
+    <!-- 上传按钮 -->
+    <div class="img-li-b--upload">
+      <el-upload
+        class="img-li-b--upl--field"
+        accept="image/*"
+        ref="upload"
+        list-type="picture-card"
+        :show-file-list="false"
+        :action="params.action"
+        :data="params.data"
+        :on-change="uploadOnChange"
+        :on-success="uploadOnSuccess"
+        :on-error="uploadOnError"
+        :on-progress="uploadOnProgress"
+      >
+        <el-button type="primary">点击上传</el-button>
+      </el-upload>
+    </div>
+
+    <!-- 放大弹窗 -->
+    <el-dialog title="" :visible.sync="isEnlargeImage" :modal-append-to-body="false" top="8%" width="60%">
+      <img @click="isEnlargeImage = false" style="width:100%;" :src="enlargeImage" />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'upload-list',
+  data() {
+    return {
+      progress: 0, //上传进度
+      pass: null, //是否上传成功
+      isEnlargeImage: false, //放大图片
+      enlargeImage: '', //放大图片地址
+      imgList: [],
+      params: {
+        action: 'http://jsonplaceholder.typicode.com/posts/',
+        data: {},
+      },
+    };
+  },
+  computed: {
+    proStatus() {
+      //上传状态
+      if (this.pass) {
+        return 'success';
+      } else if (this.pass === false) {
+        return 'exception';
+      } else {
+        return 'text';
+      }
+    },
+  },
+  methods: {
+    uploadOnProgress(e, file) {
+      //开始上传
+      console.log(e.percent, file);
+      this.progress = Math.floor(e.percent);
+    },
+    uploadOnChange(file) {
+      console.log('——————————change——————————');
+      // console.log(file)
+      if (file.status == 'ready') {
+        console.log('ready');
+        this.pass = null;
+        this.progress = 0;
+      } else if (file.status == 'fail') {
+        this.$message.error('图片上传出错,请刷新重试!');
+      }
+    },
+    uploadOnSuccess(e, file) {
+      //上传附件
+      console.log('——————————success——————————');
+      this.pass = true;
+      this.$message.success('上传成功');
+      this.imgList.push({
+        url: file.url,
+        name: '新增图片',
+      });
+    },
+    uploadOnError(e, file) {
+      console.log('——————————error——————————');
+      console.log(e);
+      this.pass = false;
+    },
+    handleFileEnlarge(_url) {
+      //放大图片
+      console.log(_url);
+      if (_url) {
+        this.enlargeImage = _url;
+        this.isEnlargeImage = !this.isEnlargeImage;
+      }
+    },
+    handleFileName(file, i) {
+      //修改名字
+      console.log(file, i);
+      let that = this;
+      this.$prompt('请输入新文件名:', '提示:', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+      })
+        .then(({ value }) => {
+          console.log(value);
+          if (!value) {
+            return false;
+          }
+          //可添加ajax
+          this.$set(this.imgList[i], 'name', value);
+          this.$message.success('操作成功');
+        })
+        .catch(() => {});
+    },
+    handleFileRemove(file, i) {
+      //删除图片
+      console.log(file, i);
+      if (!file.url) {
+        return false;
+      }
+      let that = this;
+      this.$confirm('是否删除此文件?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          //可添加ajax
+          this.$message({
+            type: 'success',
+            message: '删除成功',
+            onClose: () => {
+              that.imgList.splice(i, 1);
+            },
+          });
+        })
+        .catch(meg => console.log(meg));
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.img-list {
+  overflow: hidden;
+  width: 100%;
+  // 文件列表
+  .img-li-box {
+    float: left;
+    text-align: left;
+    position: relative;
+    display: inline-block;
+    width: 200px;
+    height: 270px;
+    padding: 5px;
+    margin: 5px 20px 20px 0;
+    border: 1px solid #d1dbe5;
+    border-radius: 4px;
+    transition: all 0.3s;
+    box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), 0 0 6px 0 rgba(0, 0, 0, 0.04);
+    // 图片
+    .img-li-b--url {
+      display: block;
+      width: 100%;
+      height: 190px;
+      margin: 0 auto;
+      border-radius: 4px;
+    }
+    // 底部
+    .img-li-b--bottom {
+      margin-top: 10px;
+      // 名称
+      .img-li-b--name {
+        width: 90%;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        height: 25px;
+        line-height: 25px;
+      }
+    }
+    // 删除按钮
+    .img-li-b--delete {
+      position: absolute;
+      bottom: 10px;
+      right: 10px;
+      color: #8492a6;
+      cursor: pointer;
+      font-size: 1.1em;
+    }
+    // 放大遮罩层
+    .img-li-b--layer {
+      position: absolute;
+      left: 0;
+      right: 0;
+      top: 0;
+      height: 200px;
+      color: #fff;
+      text-align: center;
+      z-index: 5;
+      background-color: rgba(0, 0, 0, 0.4);
+      // 放大按钮
+      i {
+        font-size: 1.6em;
+        margin-top: 80px;
+      }
+    }
+    .img-li-b--delete,
+    .img-li-b--layer {
+      opacity: 0;
+      transition: all 0.3s;
+    }
+    // 悬浮可见删除 or 放大按钮
+    &:hover {
+      .img-li-b--delete,
+      .img-li-b--layer {
+        opacity: 1;
+      }
+    }
+    // 上传进度
+    &.img-li-b--progress {
+      text-align: center;
+      padding-top: 50px;
+    }
+  }
+  // 上传按钮
+  .img-li-b--upload {
+    float: left;
+    width: 200px;
+    height: 270px;
+    display: table;
+    text-align: center;
+    .img-li-b--upl--field {
+      width: 100%;
+      display: table-cell;
+      vertical-align: middle;
+    }
+  }
+}
+</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>

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

@@ -0,0 +1,240 @@
+<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() {
+    if (this.selected.length > 0) {
+      this.defaultProcess();
+    }
+  },
+  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`, _.dropRight(this.selectList));
+          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;
+}
+/deep/.el-collapse-item__header {
+  height: 0;
+}
+/deep/.el-collapse-item__arrow {
+  margin: -10px 8px 0 auto;
+}
+</style>

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

@@ -0,0 +1,148 @@
+<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() {
+    if (this.selected.length > 0) {
+      this.defaultProcess();
+    }
+  },
+  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>
+/deep/.el-collapse-item__header {
+  height: 0;
+}
+/deep/.el-collapse-item__arrow {
+  margin: -10px 8px 0 auto;
+}
+</style>

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

@@ -0,0 +1,149 @@
+<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>
+      <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="6"><el-button type="info" @click="dialog = false">返回 </el-button></el-col>
+          <el-col :span="6" v-if="disObject.type === 'file'"><el-button @click="downLoad(disObject.url)">下载文件</el-button></el-col>
+          <el-col :span="6"><el-button type="primary" @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 res = this.isPic(duplicate.url);
+      if (this.isPic(duplicate.url)) {
+        this.disObject.type = 'pic';
+      } else {
+        this.disObject.type = 'file';
+      }
+      this.disObject.url = duplicate.url;
+      this.dialog = true;
+    },
+    defalutProcess(val) {
+      if (typeof val === 'object' && _.get(val, length) !== undefined) {
+        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 {
+        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) {
+      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>

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

@@ -0,0 +1,35 @@
+<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;
+}
+</style>

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

@@ -0,0 +1,212 @@
+<template>
+  <div id="heads">
+    <el-row>
+      <div class="headTop">
+        <a href="" class="headImg">
+          <!-- <img src="../../assets/default_logo.png" width="60" height="60" /> -->
+        </a>
+        <h3 class="headCompany">学生管理</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 class="mr14" style="color:red"> <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.xm }}
+        </span>
+        <a class="mr14">
+          <el-link @click="dialog = true" class="el-icon-edit-outline info_icon mr14">修改密码</el-link>
+          <!-- <span ></span>
+          &nbsp;修改密码 -->
+        </a>
+        <span class="mr14">|</span>
+        <a 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: () => ({
+    visible: false,
+    html: '<ul class="popoverCon"><li>没有消息</li></ul><a class="popoverBtn">点击进入信息中心<span class="el-icon-d-arrow-right info_icon"></span></a>',
+    dialog: false,
+    form: {},
+    rules: {
+      passwd: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
+    },
+  }),
+  created() {
+    this.checkUrl();
+    this.setUser();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapMutations(['setUser']),
+    ...mapActions(['userOperation']),
+    checkUrl() {
+      if (location.search.length > 0) {
+        let params = location.search.replace('?', '').split('=');
+        let key = params[0];
+        if (key.includes(`token`)) {
+          let token = params[1];
+          sessionStorage.setItem('code', token);
+          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;
+}
+</style>

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

@@ -0,0 +1,46 @@
+<template>
+  <div id="menus">
+    <el-menu :default-active="thisRouter()" class="el-menu-vertical-demo" :router="false" :default-openeds="defalutMenu" @select="to">
+      <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="/want/info/index">招聘信息</el-menu-item>
+          <!-- <el-menu-item index="/want/invite/index">企业邀约</el-menu-item> -->
+          <el-menu-item index="/want/resume/index">简历管理</el-menu-item>
+          <el-menu-item index="/want/interview/index">求职管理</el-menu-item>
+          <!-- <el-menu-item index="/want/practice/index">实习管理</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>

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

@@ -0,0 +1,55 @@
+<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) {},
+  },
+};
+</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>

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

@@ -0,0 +1,75 @@
+<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;
+  overflow: hidden;
+  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 '@/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);

+ 38 - 0
src/plugins/methods.js

@@ -0,0 +1,38 @@
+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.$toDetail = uri => {
+      let token = sessionStorage.getItem('code');
+      window.open(`http://localhost:8004${uri}&token=${token}`);
+    };
+    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 = 5;
+  },
+};
+
+Vue.use(Plugin);

+ 47 - 0
src/router.js

@@ -0,0 +1,47 @@
+import Vue from 'vue';
+import Router from 'vue-router';
+
+Vue.use(Router);
+
+export default new Router({
+  mode: 'history',
+  base: process.env.BASE_URL,
+  routes: [
+    {
+      path: '/',
+      name: 'index',
+      component: () => import('./views/index.vue'),
+    },
+    //重整理结构目录后的路由部分
+    {
+      path: '/want/info/index',
+      name: 'infoIndex',
+      component: () => import('./views/want/info/index.vue'),
+    },
+    {
+      path: '/want/invite/index',
+      name: 'inviteIndex',
+      component: () => import('./views/want/invite/index.vue'),
+    },
+    {
+      path: '/want/resume/index',
+      name: 'resumeIndex',
+      component: () => import('./views/want/resume/index.vue'),
+    },
+    {
+      path: '/want/interview/index',
+      name: 'interviewIndex',
+      component: () => import('./views/want/interview/index.vue'),
+    },
+    {
+      path: '/want/interview/detail',
+      name: 'interviwDetail',
+      component: () => import('./views/want/interview/detail.vue'),
+    },
+    {
+      path: '/want/practice/index',
+      name: 'practiceIndex',
+      component: () => import('./views/want/practice/index.vue'),
+    },
+  ],
+});

+ 198 - 0
src/store.js

@@ -0,0 +1,198 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+
+Vue.use(Vuex);
+
+const api = {
+  fairs: '/api/jobs/fairs',
+  faircorps: '/api/jobs/faircorps',
+  talks: '/api/jobs/talks',
+  infos: '/api/jobs/infos',
+  posts: '/api/jobs/posts',
+  postsInfo: '/api/jobs/posts/{id}',
+  resumesAdd: '/api/jobs/resumes',
+  resumes: '/api/jobs/resumes/{id}',
+  letters: '/api/jobs/letters',
+  lettersInfo: '/api/jobs/letters/{id}',
+  userReg: '/api/stud/registers/{id}',
+  corpInfo: '/api/corp/corps/{corpid}/info',
+  corpBase: '/api/corp/corps/{corpid}',
+  corpIdentity: '/api/corp/corps/{corpid}/identity',
+  //微信部分
+  connection: '/weixin/qrcode/create',
+  wxtoken: '/weixin/qrcode/{qrcode}/token',
+  studBind: '/api/stud/bind', //post=>query;corpid;body:mobile,wxtoken
+  studLogin: '/api/stud/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 fairsOperation({ state }, { type, data }) {
+      let { skip, limit } = data;
+      let result;
+      if (type === 'list') {
+        data = this.$toUndefined(data);
+        let { corpid } = data;
+        result = await this.$axios.$get(api.fairs, {}, { corpid: corpid, skip: skip, limit: limit });
+      }
+      if (type === 'mylist') {
+        let { schid, fairid, corpid, status } = data;
+        result = await this.$axios.$get(api.faircorps, {}, { schid: schid, fairid: fairid, corpid: corpid, status: status });
+      }
+      return result;
+    },
+    //宣讲会
+    async talksOperation({ state }, { type, data }) {
+      let { skip, limit } = data;
+      let result;
+      if (type === 'list') {
+        data = this.$toUndefined(data);
+        let { corpname, schid } = data;
+        result = await this.$axios.$get(api.talks, {}, { schid: schid, corpname: corpname, skip: skip, limit: limit });
+      }
+      return result;
+    },
+    //在线招聘
+    async jobinfoOperation({ state }, { type, data }) {
+      let { skip, limit } = data;
+      let result;
+      if (type === 'list') {
+        data = this.$toUndefined(data);
+        let { corpid, is_practice, corpname } = data;
+        result = await this.$axios.$get(api.infos, {}, { corpid: corpid, is_practice: is_practice, corpname: corpname, skip: skip, limit: limit });
+      }
+      return result;
+    },
+    //职位管理
+    async postOperation({ state }, { type, data }) {
+      let { skip, limit } = data;
+      let result;
+      if (type === 'list') {
+        data = this.$toUndefined(data);
+        let { corpid, state, corpname, is_practice } = data;
+        result = await this.$axios.$get(
+          api.posts,
+          {},
+          { corpid: corpid, state: state, corpname: corpname, is_practice: is_practice, skip: skip, limit: limit }
+        );
+      }
+      if (type === 'search') {
+        data = this.$toUndefined(data);
+        let { id } = data;
+        result = await this.$axios.$get(api.postsInfo, { id: id });
+      }
+      return result;
+    },
+    // 简历管理
+    async resumesOperation({ state }, { type, data }) {
+      let result;
+      if (type === 'add') {
+        let { info, schid, studid } = data;
+        result = await this.$axios.$post(api.resumesAdd, info, {}, { schid: schid, studid: studid });
+      }
+      if (type === 'search') {
+        let { id } = data;
+        result = await this.$axios.$get(api.resumes, { id: id });
+      }
+      if (type === 'update') {
+        let { id, info } = data;
+        result = await this.$axios.$post(api.resumes, info, { id: id });
+      }
+      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 });
+      }
+      return result;
+    },
+    //用户信息
+    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.studLogin, info, {}, { type: loginType });
+      }
+      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 };
+      }
+      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 { userid, ...info } = data;
+      let result = await this.$axios.$post(api.studBind, info, {}, { userid: userid });
+      return result;
+    },
+  },
+});
+
+const data = {
+  testItem: {
+    id: Math.random(),
+    name: 'name',
+    age: 'age',
+    tel: '13099876544',
+  },
+};

+ 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));
+  },
+};

+ 261 - 0
src/views/index.vue

@@ -0,0 +1,261 @@
+<template lang="html">
+  <div id="index">
+    <el-row style="margin:0 0 40px 0;">
+      <el-col class="top">
+        <el-col class="topLeft">
+          <el-col class="topLeftTit">求职中心</el-col>
+          <el-col class="topLeftTxt">
+            <ul>
+              <li>
+                <span class="number number_s">3</span>
+                <p>我的投递</p>
+              </li>
+              <li>
+                <span class="number number_s">0</span>
+                <p>我的面试</p>
+              </li>
+              <li>
+                <span class="number number_s">0</span>
+                <p>我的关注</p>
+              </li>
+              <li>
+                <span class="number number_s">0</span>
+                <p>我的培训</p>
+              </li>
+              <li>
+                <span class="number number_s">0</span>
+                <p>我的问卷</p>
+              </li>
+            </ul>
+          </el-col>
+        </el-col>
+        <el-col class="topRight">
+          <el-col class="topLeftTit">工作机会</el-col>
+          <el-col class="topLeftTxt">
+            <ul>
+              <li>
+                <span class="number number_s">_ _</span>
+                <p>职位职能推荐</p>
+              </li>
+              <li>
+                <span class="number number_s">0</span>
+                <p>企业邀约</p>
+              </li>
+            </ul>
+          </el-col>
+        </el-col>
+      </el-col>
+      <el-col class="main">
+        <el-col class="line">求职招聘</el-col>
+        <el-col class="newest">
+          <el-tabs type="border-card">
+            <el-tab-pane label="最新消息">
+              <ul class="newestMess">
+                <li v-for="(tag, index) in tags" :key="index" :type="tag.type">
+                  <el-link>{{ tag.name }}</el-link>
+                </li>
+              </ul>
+            </el-tab-pane>
+            <el-tab-pane label="最近浏览">
+              <ul class="newestMess">
+                <li v-for="(tag, index) in tagsBro" :key="index" :type="tag.type">
+                  <el-link>{{ tag.name }}</el-link>
+                </li>
+              </ul>
+            </el-tab-pane>
+          </el-tabs>
+        </el-col>
+      </el-col>
+      <el-col class="mainTable">
+        <el-col class="table comMajor">
+          <el-table :data="tableDataMaj" style="width: 100%">
+            <el-table-column prop="date" label="同专业关注排行榜(本月)" width="214"> </el-table-column>
+            <el-table-column prop="name" label="" width="100"> </el-table-column>
+          </el-table>
+        </el-col>
+        <el-col class="table colCol">
+          <el-table :data="tableDataCol" style="width: 100%">
+            <el-table-column prop="date" label="同学院关注排行榜(本月)" width="214"> </el-table-column>
+            <el-table-column prop="name" label="" width="100"> </el-table-column>
+          </el-table>
+        </el-col>
+        <el-col class="table colSch">
+          <el-table :data="tableDataSch" style="width: 100%">
+            <el-table-column prop="date" label="同学校关注排行榜" width="190"> </el-table-column>
+            <el-table-column prop="name" label="" width="100"> </el-table-column>
+          </el-table>
+        </el-col>
+        <el-col class="table sign">
+          <el-table :data="tableDataSign" style="width: 100%">
+            <el-table-column prop="date" label="本专业毕业生签约单位(前十)" width="540"> </el-table-column>
+            <el-table-column prop="name" label="人数" width="100"> </el-table-column>
+          </el-table>
+        </el-col>
+        <el-col class="table where">
+          <el-table :data="tableDataWhe" style="width: 100%">
+            <el-table-column prop="date" label="本专业毕业生去向" width="190"> </el-table-column>
+            <el-table-column prop="name" label="人数" width="100"> </el-table-column>
+          </el-table>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import formItem from '@/components/form-item.vue';
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: () => ({
+    tags: [
+      { name: '长春大学2020届毕业生专业设置及毕业生人数统计表' },
+      { name: '长春大学2020届毕业生专业设置及毕业生人数统计表' },
+      { name: '长春大学2020届毕业生专业设置及毕业生人数统计表' },
+      { name: '长春大学2020届毕业生专业设置及毕业生人数统计表' },
+      { name: '长春大学2020届毕业生专业设置及毕业生人数统计表' },
+    ],
+    tagsBro: [{ name: '宣讲会:辽宁鸿文教育科技有限公司' }, { name: '宣讲会:皇明太阳能股份有限公司' }, { name: '职位:部门助理' }],
+    tableDataMaj: [],
+    tableDataCol: [],
+    tableDataSch: [],
+    tableDataSign: [],
+    tableDataWhe: [],
+  }),
+  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;
+}
+.top {
+  width: 100%;
+  height: 115px;
+  margin-top: 20px;
+}
+.topLeft {
+  width: 70%;
+  height: 100px;
+  text-align: center;
+  display: inline-block;
+  background-color: #60d295;
+  border: 1px solid #60d295;
+  color: #fff;
+  margin-bottom: 15px;
+  margin-right: 18px;
+}
+.topLeftTit {
+  height: 32px;
+  line-height: 32px;
+  color: #fff;
+  font-size: 20px;
+}
+.topLeftTxt {
+  height: 56px;
+}
+.topLeftTxt ul li {
+  float: left;
+  width: 129px;
+  height: 56px;
+  text-align: center;
+  border-left: 1px solid rgba(255, 255, 255, 0.3);
+}
+.topLeftTxt ul li .number {
+  font-size: 28px;
+  color: #fff;
+}
+.topLeftTxt ul li p {
+  font-size: 14px;
+  color: #fff;
+}
+.topRight {
+  width: 28%;
+  height: 100px;
+  text-align: center;
+  display: inline-block;
+  background-color: #7cbae5;
+  border: 1px solid #6eb0dd;
+  color: #fff;
+}
+.main {
+  width: 100%;
+  min-height: 300px;
+}
+.line {
+  text-align: center;
+  font-size: 18px;
+  position: relative;
+  margin: 15px 0px;
+}
+.line:before,
+.line:after {
+  content: '';
+  width: 40%;
+  height: 1px;
+  background: #bbb;
+  display: inline-block;
+  position: absolute;
+  left: 0%;
+  top: 15px;
+}
+.line:after {
+  left: 60%;
+}
+.newest {
+  width: 100%;
+}
+.newestMess li {
+  height: 23px;
+  padding: 8px;
+  border-bottom: 1px dashed #ccc;
+}
+.newestMess li:hover {
+  background: #f6f6f6;
+}
+/deep/.el-tabs--border-card > .el-tabs__content {
+  min-height: 230px;
+}
+.mainTable {
+  width: 100%;
+  height: 200px;
+  margin: 20px 0;
+}
+.table {
+  margin-bottom: 19px;
+  min-height: 110px;
+  border: 1px solid #e7e6eb;
+}
+.comMajor {
+  width: 316px;
+  margin-right: 10px;
+}
+.colCol {
+  width: 316px;
+  margin-right: 10px;
+}
+.colSch {
+  width: 294px;
+}
+.sign {
+  width: 642px;
+  margin-right: 10px;
+}
+.where {
+  width: 294px;
+}
+</style>

+ 183 - 0
src/views/practice/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template v-slot:title>
+        <el-col :span="2">
+          <div class="demo-basic--circle">
+            <div class="block"><el-avatar :size="80" :src="circleUrl"></el-avatar></div>
+          </div>
+        </el-col>
+        <el-col style="margin-top:1rem" :span="5">
+          <el-row style="font-size: 1.5rem">姓名</el-row>
+          <el-row style="color: #666666">(编号)|学院|专业</el-row>
+        </el-col>
+      </template>
+      <template v-slot:main>
+        <el-table :data="dataList" stripe border style="width: 100%;margin-bottom:2rem;">
+          <el-table-column align="center" prop="score" label="评分项" width="150">成绩 </el-table-column>
+          <el-table-column align="center" prop="practice" label="实习表现" width="300">-</el-table-column>
+          <el-table-column align="center" prop="record" label="联系记录" width="200">-</el-table-column>
+          <el-table-column align="center" prop="plan" label="实习计划与总结" width="464">-</el-table-column>
+          <el-table-column align="center" prop="search" label="满意度调查" width="200">-</el-table-column>
+          <el-table-column align="center" prop="skill" label="技能鉴定" width="200">-</el-table-column>
+        </el-table>
+        <el-tabs v-model="activeName" type="card">
+          <el-tab-pane label="实习单位" name="1">
+            <el-form>
+              <el-form-item>
+                <el-row class="titleStyle fontColor">学生信息</el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 10rem;">
+                    性别:
+                    <el-radio v-model="radio" label="1" style="padding: 0 1rem;">男</el-radio>
+                    <el-radio v-model="radio" label="2">女</el-radio>
+                  </el-col>
+                  <el-col :span="10">身份证号码: <el-input v-model="info.input1" style="padding: 0 1rem;width: 50%"></el-input></el-col>
+                </el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 6.5rem;">
+                    学生联系电话: <el-input v-model="info.input2" style="padding: 0 1rem;width: 50%"></el-input>
+                  </el-col>
+                  <el-col :span="10">家庭联系电话: <el-input v-model="info.input3" style="padding: 0 0.12rem;width: 50%"></el-input> </el-col>
+                </el-row>
+                <el-row class="titleStyle" style=" padding: 0.4rem;padding-left:8.6rem">
+                  QQ号码:<el-input v-model="info.input4" style="padding: 0 1.25rem;width: 24.5%"></el-input>
+                </el-row>
+                <el-row class="titleStyle fontColor pUD04">
+                  单位信息
+                </el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 8.28rem;">
+                    实习单位: <el-input v-model="info.input5" style="padding: 0 1rem;width: 54.5%"></el-input>
+                  </el-col>
+                  <el-col :span="10">
+                    组织机构代码: <el-input v-model="info.input6" style="padding: 0 0.12rem;width: 50%"></el-input>
+                    <el-button icon="el-icon-search" circle></el-button>
+                  </el-col>
+                </el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 8.28rem;">
+                    单位行业: <el-select v-model="info.value1" style="padding: 0 1rem;width: 54.5%"></el-select>
+                  </el-col>
+                  <el-col :span="10">单位性质: <el-select v-model="info.value2" style="padding: 0 1.8rem;width: 50.5%"></el-select> </el-col>
+                </el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 8.28rem;">
+                    职位类别: <el-select v-model="info.value3" style="padding: 0 1rem;width: 54.5%"></el-select>
+                  </el-col>
+                  <el-col :span="10">身份证号码: <el-input v-model="info.input7" style="padding: 0 1rem;width: 50%"></el-input> </el-col>
+                </el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 5.6rem;">
+                    单位联系人电话: <el-input v-model="info.input8" style="padding: 0 1rem;width: 47.8%"></el-input>
+                  </el-col>
+                  <el-col :span="10">实习途径: <el-select v-model="info.value4" style="padding: 0 1.8rem;width: 50.5%"></el-select> </el-col>
+                </el-row>
+                <el-row class="titleStyle pUD04" style="padding-left:8.3rem">
+                  实习地点:<el-input v-model="info.input9" style="padding: 0 1.25rem;width: 78.3%"></el-input>
+                </el-row>
+                <el-row class="titleStyle fontColor pUD04">
+                  实习合同
+                </el-row>
+                <el-row class="pUD04">
+                  <uploadFile ref="uploadFile"></uploadFile>
+                </el-row>
+              </el-form-item>
+            </el-form>
+            <el-row type="flex" justify="center">
+              <el-col :span="1">
+                <el-button type="success">提交</el-button>
+              </el-col>
+            </el-row>
+          </el-tab-pane>
+        </el-tabs>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import uploadFile from '@/components/upload-file.vue';
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  components: {
+    listNormal,
+    uploadFile,
+  },
+  data: () => ({
+    dataList: [{ score: '成绩', practice: '-', record: '-', plan: '-', search: '-', skill: '-' }],
+    activeName: '1',
+    info: {},
+    select: '',
+    circleUrl: '',
+    sizeList: '',
+    input1: '',
+    input2: '',
+    input3: '',
+    input4: '',
+    input5: '',
+    input6: '',
+    input7: '',
+    input8: '',
+    input9: '',
+    value1: [],
+    value2: [],
+    value3: [],
+    value4: [],
+    radio: '',
+    listSelect: [],
+    currentPage: 1,
+    totalRow: 1,
+  }),
+  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;
+}
+.titleStyle {
+  border-bottom-style: dashed;
+  border-width: 1px;
+  border-color: #cccccc;
+}
+.fontColor {
+  font-size: 1.5rem;
+  color: green;
+}
+.pUD04 {
+  padding: 0.4rem 0;
+}
+</style>

+ 450 - 0
src/views/resume/index.vue

@@ -0,0 +1,450 @@
+<template>
+  <div id="resume">
+    <detail-layout>
+      <template v-slot:title>
+        创建简历
+      </template>
+      <template #main>
+        <el-row>
+          <el-form :model="info">
+            <el-card class="box-card" shadow="never">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="18">求职意向</el-col>
+                  <el-col :span="3"><el-button type="success" size="mini">简历完成度70%</el-button></el-col>
+                  <el-col :span="3"><el-button type="success" size="midium" icon="el-icon-view">预览</el-button></el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <form-item label="期望行业">
+                  <template>
+                    <el-select v-model="value1" :multiple="true" :filterable="true" placeholder="请选择" style="width: 100%">
+                      <el-option v-for="item in options" :key="item.value1" :label="item.label" :value="item.value"> </el-option>
+                    </el-select>
+                  </template>
+                </form-item>
+                <form-item label="期望职业"> <el-input v-model="jobs1" placeholder="程序员"></el-input></form-item>
+                <form-item label="职位分类">
+                  <template>
+                    <el-select
+                      v-model="value2"
+                      :multiple="true"
+                      :filterable="true"
+                      placeholder="请选择,最多只能选择3项"
+                      :multiple-limit="3"
+                      style="width: 100%"
+                    >
+                      <el-option v-for="item in options2" :key="item.value2" :label="item.label" :value="item.value"> </el-option>
+                    </el-select>
+                  </template>
+                </form-item>
+                <form-item label="期望城市">
+                  <template>
+                    <el-select v-model="value3" :multiple="true" :filterable="true" placeholder="长春" style="width: 100%">
+                      <el-option-group v-for="group in options3" :key="group.label" :label="group.label">
+                        <el-option v-for="item in group.options3" :key="item.value3" :label="item.label" :value="item.value"> </el-option>
+                      </el-option-group>
+                    </el-select>
+                  </template>
+                </form-item>
+                <form-item label="期望薪资">
+                  <template>
+                    <el-select v-model="value4" placeholder="请选择..." style="width: 100%">
+                      <el-option v-for="item in options4" :key="item.value4" :label="item.label" :value="item.value"> </el-option>
+                    </el-select>
+                  </template>
+                </form-item>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="18">基本信息</el-col>
+                </el-row>
+              </template>
+              <template>
+                <el-row>
+                  <form-item label="头像"> </form-item>
+                </el-row>
+              </template>
+              <el-row>
+                <form-item label="姓名"> <el-input v-model="jobs2" placeholder=" "></el-input></form-item>
+                <template>
+                  <form-item label="性别">
+                    <el-radio v-model="radio" label="1">男</el-radio>
+                    <el-radio v-model="radio" label="2">女</el-radio>
+                  </form-item>
+                </template>
+                <form-item label="籍贯"> <el-input v-model="jobs3" placeholder="长春市朝阳区"></el-input></form-item>
+                <form-item label="政治面貌">
+                  <template>
+                    <el-select v-model="value5" placeholder="请选择..." style="width: 100%">
+                      <el-option v-for="item in options5" :key="item.value5" :label="item.label" :value="item.value"> </el-option>
+                    </el-select>
+                  </template>
+                </form-item>
+                <form-item label="学校"> <el-input v-model="jobs4" placeholder="长春大学"></el-input></form-item>
+                <form-item label="所学专业"> <el-input v-model="jobs5" placeholder="计算机科学与技术"></el-input></form-item>
+                <form-item label="学历">
+                  <template>
+                    <el-select v-model="value6" placeholder="请选择..." style="width: 100%">
+                      <el-option v-for="item in options6" :key="item.value6" :label="item.label" :value="item.value"> </el-option>
+                    </el-select>
+                  </template>
+                </form-item>
+                <form-item label="手机"> <el-input v-model="jobs6" placeholder=" "></el-input></form-item>
+                <form-item label="邮箱"> <el-input v-model="jobs7" placeholder=" "></el-input></form-item>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row>
+                  <el-col :span="18">自我介绍</el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <form-item label="一句话介绍"> <el-input v-model="jobs8" placeholder=" "></el-input></form-item>
+                <form-item label="专业技能"> <el-input v-model="jobs9" placeholder=" "></el-input></form-item>
+                <el-row>
+                  <form-item label="个人详细说明">
+                    <el-input type="textarea" :rows="8" placeholder="请输入内容" v-model="textarea" maxlength="100" show-word-limit> </el-input
+                  ></form-item>
+                </el-row>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">求职意向</el-col>
+                  <el-col :span="3"><el-button type="success" size="mini" icon="el-icon-plus">新增</el-button></el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <template>
+                  <el-table :data="tableData" style="width: 100%">
+                    <el-table-column prop="school" align="left" label="毕业院校" width="450"> </el-table-column>
+                    <el-table-column prop="time" align="center" label="就读时间" width="190"> </el-table-column>
+                    <el-table-column prop="edu" align="center" label="学历" width="180"> </el-table-column>
+                    <el-table-column prop="domain" align="center" label="专业" width="180"> </el-table-column>
+                    <el-table-column align="center" label="操作">
+                      <template v-slot="scope">
+                        <el-button type="text">编辑</el-button>
+                        <el-button type="text">删除</el-button>
+                      </template>
+                    </el-table-column>
+                  </el-table>
+                </template>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">工作或项目经历</el-col>
+                  <el-col :span="3"><el-button type="success" size="mini" icon="el-icon-plus">新增</el-button></el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <template>
+                  <el-table :data="jobData" style="width: 100%">
+                    <el-table-column prop="firm" align="left" label="公司/项目" width="450"> </el-table-column>
+                    <el-table-column prop="time" align="center" label="在职/参职时间" width="290"> </el-table-column>
+                    <el-table-column prop="post" align="center" label="职位/角色" width="260"> </el-table-column>
+                    <el-table-column align="center" label="操作">
+                      <template v-slot="scope">
+                        <el-button type="text">编辑</el-button>
+                        <el-button type="text">删除</el-button>
+                      </template>
+                    </el-table-column>
+                  </el-table>
+                </template>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">证书</el-col>
+                  <el-col :span="3"><el-button type="success" size="mini" icon="el-icon-plus">新增</el-button></el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <template> </template>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">能力介绍<span style="font-weight: 700; font-size: 0.75rem;">(如您的作品,成绩单,证书等证明材料)</span></el-col>
+                  <el-col :span="3"><el-button type="success" size="mini" icon="el-icon-plus">新增</el-button></el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <template> </template>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">个人风采</el-col>
+                  <el-col :span="3"><el-button type="success" size="mini" icon="el-icon-plus">新增</el-button></el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <template> </template>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">简历附件<span style="font-weight: 700; font-size: 0.75rem;">(可上传pdf、word文档或图片文件)</span></el-col>
+                  <el-col :span="3"><el-button type="success" size="mini" icon="el-icon-plus">新增</el-button></el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <template> </template>
+              </el-row>
+            </el-card>
+          </el-form>
+          <el-row type="flex" justify="center">
+            <el-col :span="6">
+              <el-button type="success" style="width:60%">保&nbsp;&nbsp;&nbsp;&nbsp;存</el-button>
+            </el-col>
+          </el-row>
+        </el-row>
+      </template>
+    </detail-layout>
+  </div>
+</template>
+
+<script>
+import formItem from '@/components/form-item.vue';
+import detailLayout from '@/layout/detail-layout.vue';
+export default {
+  name: 'resume',
+  props: {
+    original: { type: Object, default: () => {} },
+  },
+  components: {
+    detailLayout,
+    formItem,
+  },
+  data: () => ({
+    jobs1: '',
+    jobs2: '',
+    jobs3: '',
+    jobs4: '',
+    jobs5: '',
+    jobs6: '',
+    jobs7: '',
+    jobs8: '',
+    jobs9: '',
+    textarea: '',
+    options: [
+      {
+        value: '选项1',
+        label: '农、林、牧、渔业',
+      },
+      {
+        value: '选项2',
+        label: '采矿业',
+      },
+      {
+        value: '选项3',
+        label: '制造业',
+      },
+      {
+        value: '选项4',
+        label: '电力、热力、燃气及水生产和供应业',
+      },
+      {
+        value: '选项5',
+        label: '建筑业',
+      },
+    ],
+    options2: [
+      {
+        value: '选项1',
+        label: '产品',
+      },
+      {
+        value: '选项2',
+        label: '设计',
+      },
+      {
+        value: '选项3',
+        label: '编程/IT开发',
+      },
+      {
+        value: '选项4',
+        label: '测试',
+      },
+      {
+        value: '选项5',
+        label: 'IT运维',
+      },
+    ],
+    options3: [
+      {
+        label: '全国',
+        options3: [
+          {
+            value: 'Quanguo',
+            label: '全国',
+          },
+        ],
+      },
+      {
+        label: '直辖市',
+        options3: [
+          {
+            value: 'Beijing',
+            label: '成都',
+          },
+          {
+            value: 'Shanghai',
+            label: '上海',
+          },
+          {
+            value: 'Tianjin',
+            label: '天津',
+          },
+          {
+            value: 'Chongqin',
+            label: '重庆',
+          },
+        ],
+      },
+    ],
+    options4: [
+      {
+        value: '选项1',
+        label: '请选择...',
+      },
+      {
+        value: '选项2',
+        label: '1K',
+      },
+      {
+        value: '选项3',
+        label: '2K',
+      },
+      {
+        value: '选项4',
+        label: '3K',
+      },
+      {
+        value: '选项5',
+        label: '4K',
+      },
+    ],
+    options5: [
+      {
+        value: '选项1',
+        label: '请选择...',
+      },
+      {
+        value: '选项2',
+        label: '共青团员',
+      },
+      {
+        value: '选项3',
+        label: '普通居民',
+      },
+      {
+        value: '选项4',
+        label: '中共党员',
+      },
+      {
+        value: '选项5',
+        label: '中共预备党员',
+      },
+    ],
+    options6: [
+      {
+        value: '选项1',
+        label: '请选择...',
+      },
+      {
+        value: '选项2',
+        label: '博士',
+      },
+      {
+        value: '选项3',
+        label: '硕士',
+      },
+      {
+        value: '选项4',
+        label: '本科',
+      },
+      {
+        value: '选项5',
+        label: '大专',
+      },
+      {
+        value: '选项6',
+        label: '其他',
+      },
+    ],
+    tableData: [
+      {
+        school: '长春大学-信息',
+        time: '2015年7月 至 2019年7月',
+        edu: '本科',
+        domain: '软件',
+      },
+    ],
+    jobData: [
+      {
+        firm: '长春市福瑞科技有限公司/测试软件',
+        time: '2018年12月 至 2019年5月',
+        post: '测试',
+      },
+    ],
+    radio: '1',
+    form: {
+      name: '',
+      place: '',
+      num: '',
+    },
+    dynamicValidateForm: {
+      domains: [],
+    },
+    value1: [],
+    value2: [],
+    value3: [],
+    value4: [],
+    value5: [],
+    value6: [],
+    info: {},
+    selectList1: [
+      // { label: '信息传输,软件和信息技术服务', value: '1' },
+      // { label: '采矿业', value: '2' },
+      // { label: '伐木业', value: '3' },
+      // { label: '家里蹲耶', value: '4' },
+    ],
+  }),
+  created() {},
+  mounted() {
+    this.$nextTick(() => {
+      // this.$set(this, `info`, this.original);
+    });
+  },
+  computed: {},
+  methods: {
+    removeDomain(item) {
+      var index = this.dynamicValidateForm.domains.indexOf(item);
+      if (index !== -1) {
+        this.dynamicValidateForm.domains.splice(index, 1);
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.tip {
+  color: #999999;
+}
+.rowStyle {
+  border-bottom: 1px dashed;
+}
+</style>

+ 139 - 0
src/views/want/info/context/in-jobfair.vue

@@ -0,0 +1,139 @@
+<template>
+  <div id="in-jobfair">
+    <!-- <el-row type="flex" align="middle">
+      <el-col :span="20">
+        <el-row type="flex" align="middle" justify="space-around">
+          <el-col :span="4" class="search_center">双选时间:</el-col>
+          <el-col :span="10">
+            <el-date-picker v-model="value1" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker>
+          </el-col>
+          <el-col :span="4" class="search_center">关键字:</el-col>
+          <el-col :span="8" class="search_center">
+            <el-input v-model="input" placeholder="请输入关键字"></el-input>
+          </el-col>
+        </el-row>
+      </el-col>
+      <el-col :span="4" class="search_center">
+        <el-button type="info" circle icon="el-icon-search icons" @click="search()"></el-button>
+      </el-col>
+    </el-row> -->
+    <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+      <el-row>
+        <el-table :data="list" style="width: 100%" :show-header="false">
+          <el-table-column align="left">
+            <template v-slot="scope">
+              <el-row type="flex" align="middle" justify="center">
+                <!-- <el-col :span="3">
+                  <div class="demo-basic--circle">
+                    <div><el-avatar shape="square" :size="80" :src="squareUrl"></el-avatar></div>
+                  </div>
+                </el-col> -->
+                <el-col :span="24">
+                  <el-row type="flex" align="middle" justify="center">
+                    <el-col :span="20">
+                      <el-link type="primary" @click="turnDetail(scope.row.id)">{{ scope.row.title }}</el-link>
+                    </el-col>
+                    <el-col :span="4">
+                      <span style="color: #F56C6C;">{{ scope.row.date }}</span>
+                    </el-col>
+                  </el-row>
+                  <el-row type="flex" align="bottom" justify="center">
+                    <el-col :span="8">
+                      <span style="font-size: 0.9rem; color: #999999;">主办方:{{ scope.row.schname }}</span>
+                    </el-col>
+                    <el-col :span="10">
+                      <!-- <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.address }} </span> -->
+                    </el-col>
+                    <el-col :span="8"> &nbsp; </el-col>
+                  </el-row>
+                  <el-row type="flex" align="bottom" justify="center">
+                    <el-col :span="8">
+                      <span style="font-size: 0.9rem; color: #999999;"> 地址:{{ scope.row.address }} </span>
+                    </el-col>
+                    <el-col :span="10">
+                      &nbsp;
+                    </el-col>
+                    <el-col :span="6">
+                      <!-- <i class="el-icon-view" style="padding-right: 0.3rem"></i>
+                      <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.view }} </span> -->
+                    </el-col>
+                  </el-row>
+                </el-col>
+              </el-row>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+    </el-card>
+    <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>
+  </div>
+</template>
+
+<script>
+import { mapActions, mapState } from 'vuex';
+import _ from 'lodash';
+export default {
+  name: 'in-jobfair',
+  props: {},
+  components: {},
+  data: () => ({
+    currentPage: 1,
+    totalRow: 0,
+    list: [],
+    searchInfo: {},
+  }),
+  created() {
+    this.search();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['fairsOperation']),
+    async search(page) {
+      let skip = 0;
+      if (page) {
+        skip = (page - 1) * this.$limit;
+      }
+      let result = await this.fairsOperation({ type: 'list', data: { schid: this.user.schid, skip: skip, limit: this.$limit, ...this.searchInfo } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `list`, result.data);
+        this.$set(this, `totalRow`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+
+    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 });
+    },
+    turnDetail(id) {
+      this.$emit('toDetail', `/jobfair/detail?id=${id}`);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.search_center {
+  text-align: center;
+}
+</style>

+ 147 - 0
src/views/want/info/context/in-talk.vue

@@ -0,0 +1,147 @@
+<template>
+  <div id="inTalk">
+    <el-row type="flex" align="middle">
+      <el-col :span="20">
+        <el-row type="flex" align="middle" justify="start">
+          <el-col :span="4" class="search_center">企业名称:</el-col>
+          <el-col :span="8" class="search_center">
+            <el-input v-model="searchInfo.corpname" placeholder="请输入企业名称"></el-input>
+          </el-col>
+        </el-row>
+      </el-col>
+      <el-col :span="4" class="search_center">
+        <el-button type="info" circle icon="el-icon-search icons" @click="search()"></el-button>
+      </el-col>
+    </el-row>
+    <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+      <el-row>
+        <el-table :data="list" style="width: 100%" :show-header="false">
+          <!-- <el-table-column align="left" label="图标" width="90">
+            <template v-slot="scope">
+              <el-col>
+                <div class="demo-basic--circle">
+                  <div class="block"><el-avatar shape="square" :size="80" :src="squareUrl"></el-avatar></div>
+                </div>
+              </el-col>
+            </template>
+          </el-table-column> -->
+          <el-table-column align="left" label="在职" width="350">
+            <template v-slot="scope">
+              <el-col>
+                <el-link type="primary" @click="turnDetail(scope.row.id)">{{ scope.row.corpname }}</el-link>
+              </el-col>
+              <el-col>
+                <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.schname }}</span>
+              </el-col>
+              <el-col>
+                <span style="font-size: 0.9rem; color: #999999;">
+                  {{ scope.row.address }}
+                </span>
+              </el-col>
+            </template>
+          </el-table-column>
+          <el-table-column align="left" label="公司" width="300">
+            <template v-slot="scope">
+              <el-col>
+                <span>&nbsp;&nbsp;{{ scope.row.firm }}</span>
+              </el-col>
+              <!-- <el-col>
+                <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.person }} </span>
+              </el-col>
+              <el-col>
+                <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.job }} </span>
+              </el-col> -->
+            </template>
+          </el-table-column>
+          <el-table-column align="right" label="操作">
+            <template v-slot="scope">
+              <el-col>
+                <span style="color: #F56C6C;">{{ scope.row.date }}&nbsp;{{ scope.row.time }}</span>
+              </el-col>
+              <el-col>
+                <span style="font-size: 0.9rem; color: #999999;">&nbsp;&nbsp;{{ scope.row.space }} </span>
+              </el-col>
+              <!-- <el-col>
+                <i class="el-icon-view" style="padding-right: 0.3rem"></i>
+                <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.team_size }} </span>
+              </el-col> -->
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+    </el-card>
+    <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>
+  </div>
+</template>
+
+<script>
+import { mapActions, mapState } from 'vuex';
+import _ from 'lodash';
+export default {
+  name: 'inTalk',
+  props: {},
+  components: {},
+  data: () => ({
+    currentPage: 1,
+    totalRow: 0,
+    searchInfo: {},
+    list: [],
+  }),
+  created() {
+    this.search();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['talksOperation']),
+    async search(page) {
+      let skip = 0;
+      if (page) {
+        skip = (page - 1) * this.$limit;
+      }
+      let result = await this.talksOperation({
+        type: 'list',
+        data: { schid: this.user.schid, skip: skip, limit: this.$limit, ...this.searchInfo },
+      });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `list`, result.data);
+        this.$set(this, `totalRow`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+
+    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 });
+    },
+    turnDetail(id) {
+      this.$emit('toDetail', `/talk/detail?id=${id}`);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.search_center {
+  text-align: center;
+}
+</style>

+ 110 - 0
src/views/want/info/context/internship.vue

@@ -0,0 +1,110 @@
+<template>
+  <div id="internShip">
+    <el-row type="flex" align="middle">
+      <el-col :span="20">
+        <el-row type="flex" align="middle" justify="start">
+          <el-col :span="4" class="search_center">企业名称:</el-col>
+          <el-col :span="8"><el-input size="small" v-model="searchInfo.corpname"></el-input></el-col>
+        </el-row>
+      </el-col>
+      <el-col :span="4" class="search_center">
+        <el-button type="info" circle icon="el-icon-search icons" @click="search"></el-button>
+      </el-col>
+    </el-row>
+    <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+      <el-row>
+        <el-table :data="list" style="width: 100%" :show-header="false">
+          <el-table-column align="left" label="在职" width="450">
+            <template v-slot="scope">
+              <el-col>
+                <el-link type="primary" @click="turnDetail(scope.row.id)">{{ scope.row.job_name }}</el-link>
+                <span style="padding-left: 1rem; font-size: 0.9rem; color: #999999;">{{ scope.row.end_date }}</span>
+              </el-col>
+              <el-col>
+                <span style="color: #F56C6C;">{{ scope.row.salary.text }}</span>
+              </el-col>
+              <el-col
+                ><span style="display:inline-block; width: 335px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;" :title="scope.row.city">
+                  {{ scope.row.city }}/{{ scope.row.xl_req }}
+                </span></el-col
+              >
+            </template>
+          </el-table-column>
+          <el-table-column align="left" label="公司" width="350">
+            <template v-slot="scope">
+              <el-col
+                ><span>{{ scope.row.corpname }}</span></el-col
+              >
+              <el-col
+                ><span style="font-size: 0.9rem;">{{ scope.row.job_number }} </span></el-col
+              >
+              <el-col
+                ><span style="font-size: 0.9rem;">{{ scope.row.category }} </span></el-col
+              >
+            </template>
+          </el-table-column>
+          <el-table-column align="center" label="操作">
+            <template v-slot="scope">
+              <!-- <el-button type="success" size="small" icon="el-icon-upload">投递简历</el-button> -->
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+    </el-card>
+    <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>
+  </div>
+</template>
+
+<script>
+import { mapActions, mapState } from 'vuex';
+import _ from 'lodash';
+export default {
+  name: 'internShip',
+  props: {},
+  components: {},
+  data: () => ({
+    currentPage: 1,
+    totalRow: 0,
+    searchInfo: {},
+    list: [],
+  }),
+  created() {
+    this.search();
+  },
+  computed: {},
+  methods: {
+    ...mapActions(['postOperation']),
+    async search(page) {
+      let skip = 0;
+      if (page) {
+        skip = (page - 1) * this.$limit;
+      }
+      let result = await this.postOperation({ type: 'list', data: { is_practice: 1, ...this.searchInfo, skip: skip, limit: this.$limit } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `list`, result.data);
+        this.$set(this, `totalRow`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    turnDetail(id) {
+      this.$emit('toDetail', `/jobs/detail?id=${id}`);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.search_center {
+  text-align: center;
+}
+</style>

+ 123 - 0
src/views/want/info/context/jobinfo.vue

@@ -0,0 +1,123 @@
+<template>
+  <div id="jobinfo">
+    <el-row type="flex" align="middle">
+      <el-col :span="20">
+        <el-row type="flex" align="middle" justify="space-around">
+          <el-col :span="4" class="search_center">招聘类型:</el-col>
+          <el-col :span="10">
+            <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-date-picker v-model="value1" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker> -->
+          </el-col>
+          <el-col :span="4" class="search_center">企业名称:</el-col>
+          <el-col :span="8" class="search_center">
+            <el-input v-model="searchInfo.corpname" placeholder="请输入企业名称"></el-input>
+          </el-col>
+        </el-row>
+      </el-col>
+      <el-col :span="4" class="search_center">
+        <el-button type="info" circle icon="el-icon-search icons" @click="search()"></el-button>
+      </el-col>
+    </el-row>
+    <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+      <el-row>
+        <el-table :data="list" style="width: 100%" :show-header="false">
+          <el-table-column align="left">
+            <template v-slot="scope">
+              <el-row type="flex" align="middle" justify="center">
+                <el-col :span="3">
+                  <div class="demo-basic--circle">
+                    <div><el-avatar shape="square" :size="80" :src="squareUrl"></el-avatar></div>
+                  </div>
+                </el-col>
+                <el-col :span="21">
+                  <el-row type="flex" align="middle" justify="center">
+                    <el-col :span="18">
+                      <el-link type="primary" @click="turnDetail(scope.row.id)">{{ scope.row.title }}</el-link>
+                    </el-col>
+                    <el-col :span="6">
+                      <span style="color: #F56C6C;">{{ scope.row.date }}</span>
+                    </el-col>
+                  </el-row>
+                  <el-row type="flex" align="bottom" justify="center">
+                    <el-col :span="8"> &nbsp; </el-col>
+                    <el-col :span="10"> &nbsp; </el-col>
+                    <el-col :span="8"> &nbsp; </el-col>
+                  </el-row>
+                  <el-row type="flex" align="bottom" justify="center">
+                    <el-col :span="8"> &nbsp; </el-col>
+                    <el-col :span="10">
+                      &nbsp;
+                    </el-col>
+                    <!-- <el-col :span="6">
+                      <i class="el-icon-view" style="padding-right: 0.3rem"></i>
+                      <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.view }} </span>
+                    </el-col> -->
+                  </el-row>
+                </el-col>
+              </el-row>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+    </el-card>
+    <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>
+  </div>
+</template>
+
+<script>
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'jobinfo',
+  props: {},
+  components: {},
+  data: () => ({
+    currentPage: 1,
+    totalRow: 0,
+    list: [],
+    searchInfo: {},
+    squareUrl: '',
+  }),
+  created() {
+    this.search();
+  },
+  computed: {},
+  methods: {
+    ...mapActions(['jobinfoOperation']),
+    async search(page) {
+      let skip = 0;
+      if (page) {
+        skip = (page - 1) * this.$limit;
+      }
+      let result = await this.jobinfoOperation({ type: 'list', data: { skip: skip, limit: this.$limit, ...this.searchInfo } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `list`, result.data);
+        this.$set(this, `totalRow`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    turnDetail(id) {
+      this.$emit('toDetail', `/jobinfo/detail?id=${id}`);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.search_center {
+  text-align: center;
+}
+</style>

+ 122 - 0
src/views/want/info/context/jobs.vue

@@ -0,0 +1,122 @@
+<template>
+  <div id="jobs">
+    <el-row type="flex" align="middle">
+      <el-col :span="20">
+        <el-row type="flex" align="middle" justify="start">
+          <el-col :span="4" class="search_center">企业名称:</el-col>
+          <el-col :span="8"><el-input size="small" v-model="searchInfo.corpname"></el-input></el-col>
+        </el-row>
+        <!-- <el-row type="flex" align="middle" justify="space-around" style="margin-top:1rem;">
+          <el-col :span="3" class="search_center">薪资:</el-col>
+          <el-col :span="4"> <el-input v-model="low" placeholder="最低(单位K)"></el-input></el-col>
+          <el-col :span="1" class="search_center">至</el-col>
+          <el-col :span="4">
+            <el-input v-model="high" placeholder="最高(单位K)"></el-input>
+          </el-col>
+          <el-col :span="4" class="search_center">关键字:</el-col>
+          <el-col :span="8" class="search_center">
+            <el-input v-model="input" placeholder="请输入关键字"></el-input>
+          </el-col>
+        </el-row> -->
+      </el-col>
+      <el-col :span="4" class="search_center">
+        <el-button type="info" circle icon="el-icon-search icons" @click="search()"></el-button>
+      </el-col>
+    </el-row>
+    <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+      <el-row>
+        <el-table :data="list" style="width: 100%" :show-header="false">
+          <el-table-column align="left" label="在职" width="450">
+            <template v-slot="scope">
+              <el-col>
+                <el-link type="primary" @click="turnDetail(scope.row.id)">{{ scope.row.job_name }}</el-link>
+                <span style="padding-left: 1rem; font-size: 0.9rem; color: #999999;">{{ scope.row.end_date }}</span>
+              </el-col>
+              <el-col>
+                <span style="color: #F56C6C;">{{ scope.row.salary.text }}</span>
+              </el-col>
+              <el-col
+                ><span style="display:inline-block; width: 335px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;" :title="scope.row.city">
+                  {{ scope.row.city }}/{{ scope.row.xl_req }}
+                </span></el-col
+              >
+            </template>
+          </el-table-column>
+          <el-table-column align="left" label="公司" width="350">
+            <template v-slot="scope">
+              <el-col
+                ><span>{{ scope.row.corpname }}</span></el-col
+              >
+              <el-col
+                ><span style="font-size: 0.9rem;">{{ scope.row.job_number }} </span></el-col
+              >
+              <el-col
+                ><span style="font-size: 0.9rem;">{{ scope.row.category }} </span></el-col
+              >
+            </template>
+          </el-table-column>
+          <el-table-column align="center" label="操作">
+            <template v-slot="scope">
+              <!-- <el-button type="success" size="small" icon="el-icon-upload">投递简历</el-button> -->
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+    </el-card>
+    <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>
+  </div>
+</template>
+
+<script>
+import { mapActions, mapState } from 'vuex';
+import _ from 'lodash';
+export default {
+  name: 'jobs',
+  props: {},
+  components: {},
+  data: () => ({
+    currentPage: 1,
+    totalRow: 0,
+    searchInfo: {},
+    list: [],
+  }),
+  created() {
+    this.search();
+  },
+  computed: {},
+  methods: {
+    ...mapActions(['postOperation']),
+    async search(page) {
+      let skip = 0;
+      if (page) {
+        skip = (page - 1) * this.$limit;
+      }
+      let result = await this.postOperation({ type: 'list', data: { is_practice: 0, ...this.searchInfo, skip: skip, limit: this.$limit } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `list`, result.data);
+        this.$set(this, `totalRow`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    turnDetail(id) {
+      this.$emit('toDetail', `/jobs/detail?id=${id}`);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.search_center {
+  text-align: center;
+}
+</style>

+ 136 - 0
src/views/want/info/context/out-jobfair.vue

@@ -0,0 +1,136 @@
+<template>
+  <div id="out-jobfair">
+    <!-- <el-row type="flex" align="middle">
+      <el-col :span="20">
+        <el-row type="flex" align="middle" justify="space-around">
+          <el-col :span="4" class="search_center">双选时间:</el-col>
+          <el-col :span="10">
+            <el-date-picker v-model="value1" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker>
+          </el-col>
+          <el-col :span="4" class="search_center">关键字:</el-col>
+          <el-col :span="8" class="search_center">
+            <el-input v-model="input" placeholder="请输入关键字"></el-input>
+          </el-col>
+        </el-row>
+      </el-col>
+      <el-col :span="4" class="search_center">
+        <el-button type="info" circle icon="el-icon-search icons" @click="search()"></el-button>
+      </el-col>
+    </el-row> -->
+    <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+      <el-row>
+        <el-table :data="list" style="width: 100%" :show-header="false">
+          <el-table-column align="left">
+            <template v-slot="scope">
+              <el-row type="flex" align="middle" justify="center">
+                <!-- <el-col :span="3">
+                  <div class="demo-basic--circle">
+                    <div><el-avatar shape="square" :size="80" :src="squareUrl"></el-avatar></div>
+                  </div>
+                </el-col> -->
+                <el-col :span="24">
+                  <el-row type="flex" align="middle" justify="center">
+                    <el-col :span="20">
+                      <el-link type="primary" @click="turnDetail(scope.row.id)">{{ scope.row.title }}</el-link>
+                    </el-col>
+                    <el-col :span="4">
+                      <span style="color: #F56C6C;">{{ scope.row.date }}</span>
+                    </el-col>
+                  </el-row>
+                  <el-row type="flex" align="bottom" justify="center">
+                    <el-col :span="8">
+                      <span style="font-size: 0.9rem; color: #999999;">主办方:{{ scope.row.schname }}</span>
+                    </el-col>
+                    <el-col :span="10">
+                      <!-- <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.address }} </span> -->
+                    </el-col>
+                    <el-col :span="8"> &nbsp; </el-col>
+                  </el-row>
+                  <el-row type="flex" align="bottom" justify="center">
+                    <el-col :span="8">
+                      <span style="font-size: 0.9rem; color: #999999;"> 地址:{{ scope.row.address }} </span>
+                    </el-col>
+                    <el-col :span="10">
+                      &nbsp;
+                    </el-col>
+                    <el-col :span="6">
+                      <!-- <i class="el-icon-view" style="padding-right: 0.3rem"></i>
+                      <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.view }} </span> -->
+                    </el-col>
+                  </el-row>
+                </el-col>
+              </el-row>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+    </el-card>
+    <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>
+  </div>
+</template>
+
+<script>
+import { mapActions, mapState } from 'vuex';
+import _ from 'lodash';
+export default {
+  name: 'out-jobfair',
+  props: {},
+  components: {},
+  data: () => ({
+    currentPage: 1,
+    totalRow: 0,
+    list: [],
+    searchInfo: {},
+  }),
+  created() {
+    this.search();
+  },
+  computed: {},
+  methods: {
+    ...mapActions(['fairsOperation']),
+    async search(page) {
+      let skip = 0;
+      if (page) {
+        skip = (page - 1) * this.$limit;
+      }
+      let result = await this.fairsOperation({ type: 'list', data: { skip: skip, limit: this.$limit, ...this.searchInfo } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `list`, result.data);
+        this.$set(this, `totalRow`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    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 });
+    },
+    turnDetail(id) {
+      this.$emit('toDetail', `/jobfair/detail?id=${id}`);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.search_center {
+  text-align: center;
+}
+</style>

+ 141 - 0
src/views/want/info/context/out-talk.vue

@@ -0,0 +1,141 @@
+<template>
+  <div id="fulltime">
+    <el-row type="flex" align="middle">
+      <el-col :span="20">
+        <el-row type="flex" align="middle" justify="start">
+          <el-col :span="4" class="search_center">企业名称:</el-col>
+          <el-col :span="8" class="search_center">
+            <el-input v-model="searchInfo.corpname" placeholder="请输入企业名称"></el-input>
+          </el-col>
+        </el-row>
+      </el-col>
+      <el-col :span="4" class="search_center">
+        <el-button type="info" circle icon="el-icon-search icons" @click="search()"></el-button>
+      </el-col>
+    </el-row>
+    <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+      <el-row>
+        <el-table :data="list" style="width: 100%" :show-header="false">
+          <!-- <el-table-column align="left" label="图标" width="90">
+            <template v-slot="scope">
+              <el-col>
+                <div class="demo-basic--circle">
+                  <div class="block"><el-avatar shape="square" :size="80" :src="squareUrl"></el-avatar></div>
+                </div>
+              </el-col>
+            </template>
+          </el-table-column> -->
+          <el-table-column align="left" label="在职" width="350">
+            <template v-slot="scope">
+              <el-col>
+                <el-link type="primary" @click="turnDetail(scope.row.id)">{{ scope.row.corpname }}</el-link>
+              </el-col>
+              <el-col>
+                <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.schname }}</span>
+              </el-col>
+              <el-col>
+                <span style="font-size: 0.9rem; color: #999999;">
+                  {{ scope.row.address }}
+                </span>
+              </el-col>
+            </template>
+          </el-table-column>
+          <el-table-column align="left" label="公司" width="300">
+            <template v-slot="scope">
+              <el-col>
+                <span>&nbsp;&nbsp;{{ scope.row.firm }}</span>
+              </el-col>
+              <!-- <el-col>
+                <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.person }} </span>
+              </el-col>
+              <el-col>
+                <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.job }} </span>
+              </el-col> -->
+            </template>
+          </el-table-column>
+          <el-table-column align="right" label="操作">
+            <template v-slot="scope">
+              <el-col>
+                <span style="color: #F56C6C;">{{ scope.row.date }}&nbsp;{{ scope.row.time }}</span>
+              </el-col>
+              <el-col>
+                <span style="font-size: 0.9rem; color: #999999;">&nbsp;&nbsp;{{ scope.row.space }} </span>
+              </el-col>
+              <!-- <el-col>
+                <i class="el-icon-view" style="padding-right: 0.3rem"></i>
+                <span style="font-size: 0.9rem; color: #999999;">{{ scope.row.team_size }} </span>
+              </el-col> -->
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+    </el-card>
+    <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>
+  </div>
+</template>
+
+<script>
+import { mapActions, mapState } from 'vuex';
+import _ from 'lodash';
+export default {
+  name: 'out-talk',
+  props: {},
+  components: {},
+  data: () => ({
+    currentPage: 1,
+    totalRow: 0,
+    searchInfo: {},
+    list: [],
+  }),
+  created() {
+    this.search();
+  },
+  computed: {},
+  methods: {
+    ...mapActions(['talksOperation']),
+    async search(page) {
+      let skip = 0;
+      if (page) {
+        skip = (page - 1) * this.$limit;
+      }
+      let result = await this.talksOperation({ type: 'list', data: { skip: skip, limit: this.$limit, ...this.searchInfo } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `list`, result.data);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+
+    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 });
+    },
+    turnDetail(id) {
+      this.$emit('toDetail', `/talk/detail?id=${id}`);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.search_center {
+  text-align: center;
+}
+</style>

+ 121 - 0
src/views/want/info/index.vue

@@ -0,0 +1,121 @@
+<template>
+  <div id="index">
+    <list-tab :tab="tabsTitle" style="margin:0 0 20px 0;">
+      <template v-slot:title>
+        <el-col :span="24" style="margin-bottom:0.5rem;">招聘信息 </el-col>
+      </template>
+      <template v-slot:main1>
+        <jobs @toDetail="toDetail"></jobs>
+      </template>
+      <template v-slot:main2>
+        <internship @toDetail="toDetail"></internship>
+      </template>
+      <template v-slot:main3>
+        <in-talk @toDetail="toDetail"></in-talk>
+      </template>
+      <template v-slot:main4>
+        <out-talk @toDetail="toDetail"></out-talk>
+      </template>
+      <template v-slot:main5>
+        <in-jobfair @toDetail="toDetail"></in-jobfair>
+      </template>
+      <template v-slot:main6>
+        <out-jobfair @toDetail="toDetail"></out-jobfair>
+      </template>
+      <template v-slot:main7>
+        <jobinfo @toDetail="toDetail"></jobinfo>
+      </template>
+    </list-tab>
+  </div>
+</template>
+
+<script>
+import jobs from './context/jobs.vue';
+import internship from './context/internship.vue';
+import inTalk from './context/in-talk.vue';
+import outTalk from './context/out-talk.vue';
+import jobinfo from './context/jobinfo.vue';
+import inJobfair from './context/in-jobfair.vue';
+import outJobfair from './context/out-jobfair.vue';
+import listTab from '@/layout/list-tab.vue';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {
+    listTab,
+    jobs,
+    internship,
+    inTalk,
+    outTalk,
+    jobinfo,
+    inJobfair,
+    outJobfair,
+  },
+  data: () => ({
+    tabsTitle: ['全职岗位', '实习岗位', '校内宣讲会', '校外宣讲会', '校内双选会', '校外双选会', '在线招聘'],
+    select: '',
+    dataList1: [],
+    totalRow1: 21878,
+    pageIds1: [],
+    dataList2: [],
+    totalRow2: 638,
+    pageIds2: [],
+    dataList3: [],
+    totalRow3: 364,
+    pageIds3: [],
+    dataList4: [],
+    totalRow4: 5230,
+    pageIds4: [],
+    dataList5: [],
+    totalRow5: 14,
+    pageIds5: [],
+    dataList6: [],
+    totalRow6: 163,
+    pageIds6: [],
+    dataList7: [],
+    totalRow7: 3423,
+    pageIds7: [],
+  }),
+  computed: {
+    ...mapState(['user']),
+  },
+  handleCurrentChange(val) {
+    this.currentPage = val;
+    this.$emit('changePage', { type: this.type, currentPage: this.currentPage });
+  },
+  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);
+      this.$set(this, `dataList4`, result);
+      this.$set(this, `dataList5`, result);
+      this.$set(this, `dataList6`, result);
+      this.$set(this, `dataList7`, result);
+    },
+    changePage({ type, currentPage }) {
+      //type=>哪个标签页
+      //currentPage=>标签页的当前页,用来计算skip的
+      console.log(type, currentPage);
+      //根据type查询对应的数据
+    },
+    selectInfo({ type, ids }) {
+      //type=>哪个标签页
+      //ids,选择的id
+      console.log(type, ids);
+      //缓存在本组件中(this.$set在本组件中),为之后的批量操作使用
+    },
+    toDetail(uri) {
+      this.$toDetail(uri);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 191 - 0
src/views/want/interview/detail.vue

@@ -0,0 +1,191 @@
+<template>
+  <div id="invite">
+    <list-normal>
+      <template v-slot:title>
+        <el-button icon="el-icon-arrow-left" @click="$router.push('/want/interview/index')" circle></el-button>
+        我的面试-详情
+      </template>
+      <template v-slot:main>
+        <el-card class="box-card">
+          <el-row>
+            <el-col :span="19">
+              <el-row>{{ postInfo.corpname }}</el-row>
+              <el-row>{{ postInfo.job_name }}</el-row>
+            </el-col>
+
+            <el-col :span="5">
+              <div class="demo-basic--circle">
+                <div class="block">
+                  <el-avatar :size="80" shape="square" fit="fill" :src="corpInfo.logo_url"></el-avatar>
+                </div>
+              </div>
+            </el-col>
+
+            <el-row>
+              <el-col :span="14">
+                <i class="el-icon-coin" style="color:#666666;"></i>
+                <span style="color:red; padding-left: 0.5rem;margin-right:2rem;">
+                  {{ postInfo.salary && postInfo.salary.text }}
+                </span>
+                <i class="el-icon-location" style="color:#909399;"></i>
+                <span style="padding-left: 0.5rem;color:#606266">
+                  {{ postInfo.city }}
+                </span>
+                <i class="el-icon-s-management" style="padding-left: 2rem;color:#909399"></i>
+                <span style="padding-left: 0.5rem;color:#606266">{{ postInfo.xl_req }}</span>
+                <i class="el-icon-s-custom" style="padding-left: 2rem;color:#909399"></i>
+                <span style="padding-left: 0.5rem;color:#606266">{{ postInfo.job_number }}人</span>
+              </el-col>
+              <el-col :span="4" :offset="4" style="text-align:center">
+                <el-link type="primary">{{ corpInfo.corpname }}</el-link>
+              </el-col>
+            </el-row>
+            <el-row style="margin-top:0.5rem">
+              <el-col :span="14">
+                <el-tag type="info" v-for="(item, index) in getTags(postInfo.job_tag)" :key="index" class="mR05">{{ item }}</el-tag>
+              </el-col>
+              <!-- <el-col :span="4" :offset="4"> <span style=" font-size:small;">提高大学生就业率,为大学生就业择业诚信服务</span></el-col> -->
+            </el-row>
+            <el-row>
+              <span style=" font-size:small;color:#C0C4CC">发布时间:{{ postInfo.meta && postInfo.meta.createdAt | stringDate }}</span>
+              <span style=" font-size:small;color:#C0C4CC;padding-left: 4rem;">截止时间:{{ postInfo.end_date }}</span>
+            </el-row>
+            <el-row class="tabStyle"><span class="pL05">岗位职责</span></el-row>
+            <el-row>
+              <span class="list_info">{{ postInfo.job_req }}</span>
+            </el-row>
+            <el-row class="tabStyle"><span class="pL05">岗位要求</span></el-row>
+            <el-row>
+              <span class="list_info">{{ postInfo.job_req }}</span>
+            </el-row>
+            <el-row class="tabStyle"><span class="pL05">投递说明</span></el-row>
+            <el-row>
+              <span class="list_info">{{ postInfo.apply_intro }}</span>
+            </el-row>
+            <el-row class="tabStyle"><span class="pL05">相关专业</span></el-row>
+            <el-row>
+              <el-col>
+                <span class="list_info" v-for="(item, index) in getTags(postInfo.zy_req)" :key="index">{{ item }}</span>
+              </el-col>
+            </el-row>
+            <el-row class="lastTab">
+              <span class="pL05">招聘过程</span>
+            </el-row>
+            <el-row style="border-left-style:solid; border-color:green;margin-top:2rem;"></el-row>
+            <el-row>
+              <!-- 这里的判断也不是那么简单,需要判断=>更新进行中和完成的状态 -->
+              <el-steps :active="1" align-center finish-status="success" :process-status="status">
+                <el-step title="投递"></el-step>
+                <!-- <el-step title="审查"></el-step> -->
+                <!-- <el-step title="通过"></el-step> -->
+                <el-step title="录用"></el-step>
+              </el-steps>
+            </el-row>
+            <!-- <el-row type="flex" justify="center" align="center">
+              <el-col :span="18"><span style="padding-left: 0.65rem;">您的简历不合格</span></el-col>
+              <el-col :span="6"><span style="text-align:right;font-size:small;color:#C0C4CC;">2019年7月18日 14:33</span></el-col>
+            </el-row> -->
+          </el-row>
+        </el-card>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'invite',
+  components: {
+    listNormal,
+  },
+  data: () => ({
+    squareUrl: '',
+    info: {},
+    postInfo: {},
+    corpInfo: {},
+    status: 'wait',
+  }),
+  created() {
+    this.search();
+  },
+  methods: {
+    ...mapActions(['lettersOperation', 'postOperation', 'corpOperation']),
+    async search() {
+      let result = await this.lettersOperation({ type: 'search', data: { id: this.$route.query.id } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `info`, result.data);
+        this.searchPost();
+        this.checkStatus();
+      }
+    },
+    async searchPost() {
+      let result = await this.postOperation({ type: 'search', data: { id: this.info.post_id } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `postInfo`, result.data);
+        this.searchCorp();
+      }
+    },
+    async searchCorp() {
+      let { info, base, identity } = await this.corpOperation({ type: 'search', data: { corpid: this.postInfo.corpid } });
+      this.corpInfo = { ...info, ...base, ...identity };
+    },
+    getTags(data) {
+      if (data && data.length > 0) {
+        let arr = data.split(',');
+        return arr;
+      }
+    },
+    checkStatus() {
+      let status = `${this.info.status}`;
+      let result = status === `0` ? 'process' : status === `1` ? 'success' : 'error';
+      this.$set(this, `status`, result);
+    },
+  },
+  filters: {
+    stringDate(date) {
+      let newDate = new Date(date);
+      return newDate
+        .toLocaleDateString()
+        .replace('/', '-')
+        .replace('/', '-');
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.icons {
+  zoom: 2;
+  position: relative;
+  top: 0.5rem;
+}
+.mR05 {
+  margin-right: 0.5rem;
+}
+.tabStyle {
+  border-left-style: solid;
+  border-color: green;
+  margin-top: 4rem;
+}
+.lastTab {
+  border-left-style: solid;
+  border-color: green;
+  margin-top: 2rem;
+  background-color: #e6e6e6;
+  height: 2rem;
+}
+.pL05 {
+  padding-left: 0.5rem;
+}
+.list_info {
+  font-size: small;
+  padding-left: 0.65rem;
+}
+</style>

+ 107 - 0
src/views/want/interview/index.vue

@@ -0,0 +1,107 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template v-slot:title>
+        我的面试
+      </template>
+      <template v-slot:main>
+        <el-table :data="list" stripe border style="width: 100%">
+          <el-table-column align="center" type="index" label="序号" width="50"> </el-table-column>
+          <el-table-column prop="corpname" label="企业名称" width="300"></el-table-column>
+          <el-table-column prop="name" label="职位名称" width="200">
+            <template v-slot="scoped">
+              <el-button type="text" @click="turnDetail(scoped.row.post_id)">{{ scoped.row.title }}</el-button>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" prop="status" label="求职状态">
+            <template v-slot="scoped">
+              <el-link :type="`${scoped.row.status}` === '0' ? '' : `${scoped.row.status}` === '1' ? 'success' : 'danger'" :underline="false">
+                {{ `${scoped.row.status}` === '0' ? '已投递' : `${scoped.row.status}` === '1' ? '已接收' : '已拒绝' }}
+              </el-link>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" prop="operate" label="操作" width="150">
+            <template v-slot="scoped">
+              <el-button type="text" @click="$router.push({ path: '/want/interview/detail', query: { id: scoped.row.id } })">详情</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 _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  components: {
+    listNormal,
+  },
+  data: () => ({
+    list: [],
+    listSelect: [],
+    currentPage: 1,
+    totalRow: 0,
+  }),
+  created() {
+    this.search();
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['lettersOperation']),
+    async search(page) {
+      let skip = 0;
+      if (page) {
+        skip = (page - 1) * this.$limit;
+      }
+      let result = await this.lettersOperation({ type: 'list', data: { skip: skip, limit: this.$limit, studname: this.user.xm } });
+      if (`${result.errcode}` === '0') {
+        this.$set(this, `list`, result.data);
+        this.$set(this, `totalRow`, result.total);
+      } else {
+        this.$message.error(result.errmsg ? result.errmsg : 'error');
+      }
+    },
+    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();
+    },
+    turnDetail(id) {
+      this.$toDetail(`/jobs/detail?id=${id}`);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.row_pagination {
+  text-align: right;
+  margin-top: 1rem;
+}
+.icons {
+  zoom: 2;
+  position: relative;
+  top: 0.5rem;
+}
+</style>

+ 106 - 0
src/views/want/invite/index.vue

@@ -0,0 +1,106 @@
+<template>
+  <div id="invite">
+    <list-normal>
+      <template v-slot:title>
+        企业邀约
+      </template>
+      <template v-slot:functionBar>
+        <el-col :span="8">
+          <el-input v-model="input" placeholder="请输入公司名称、HR姓名或HR手机号"> </el-input>
+        </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" border :stripe="true" @select="selectInfo" @select-all="selectInfo">
+          <el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
+          <el-table-column align="left" label="名称" prop="name" width="300"></el-table-column>
+          <el-table-column align="center" label="HR" prop="hr"></el-table-column>
+          <el-table-column align="center" label="发布时间" prop="time"></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';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'invite',
+  components: {
+    listNormal,
+  },
+  data: () => ({
+    dataList: [
+      {
+        name: 'HR1',
+        hr: '人力资源管理',
+        time: '2019-07-25',
+      },
+    ],
+    input: '',
+    select: '',
+    listSelect: [],
+    currentPage: 1,
+    totalRow: 5,
+  }),
+  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>

+ 183 - 0
src/views/want/practice/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <div id="index">
+    <list-normal>
+      <template v-slot:title>
+        <el-col :span="2">
+          <div class="demo-basic--circle">
+            <div class="block"><el-avatar :size="80" :src="circleUrl"></el-avatar></div>
+          </div>
+        </el-col>
+        <el-col style="margin-top:1rem" :span="5">
+          <el-row style="font-size: 1.5rem">姓名</el-row>
+          <el-row style="color: #666666">(编号)|学院|专业</el-row>
+        </el-col>
+      </template>
+      <template v-slot:main>
+        <el-table :data="dataList" stripe border style="width: 100%;margin-bottom:2rem;">
+          <el-table-column align="center" prop="score" label="评分项" width="150">成绩 </el-table-column>
+          <el-table-column align="center" prop="practice" label="实习表现" width="300">-</el-table-column>
+          <el-table-column align="center" prop="record" label="联系记录" width="200">-</el-table-column>
+          <el-table-column align="center" prop="plan" label="实习计划与总结" width="464">-</el-table-column>
+          <el-table-column align="center" prop="search" label="满意度调查" width="200">-</el-table-column>
+          <el-table-column align="center" prop="skill" label="技能鉴定" width="200">-</el-table-column>
+        </el-table>
+        <el-tabs v-model="activeName" type="card">
+          <el-tab-pane label="实习单位" name="1">
+            <el-form>
+              <el-form-item>
+                <el-row class="titleStyle fontColor">学生信息</el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 10rem;">
+                    性别:
+                    <el-radio v-model="radio" label="1" style="padding: 0 1rem;">男</el-radio>
+                    <el-radio v-model="radio" label="2">女</el-radio>
+                  </el-col>
+                  <el-col :span="10">身份证号码: <el-input v-model="info.input1" style="padding: 0 1rem;width: 50%"></el-input></el-col>
+                </el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 6.5rem;">
+                    学生联系电话: <el-input v-model="info.input2" style="padding: 0 1rem;width: 50%"></el-input>
+                  </el-col>
+                  <el-col :span="10">家庭联系电话: <el-input v-model="info.input3" style="padding: 0 0.12rem;width: 50%"></el-input> </el-col>
+                </el-row>
+                <el-row class="titleStyle" style=" padding: 0.4rem;padding-left:8.6rem">
+                  QQ号码:<el-input v-model="info.input4" style="padding: 0 1.25rem;width: 24.5%"></el-input>
+                </el-row>
+                <el-row class="titleStyle fontColor pUD04">
+                  单位信息
+                </el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 8.28rem;">
+                    实习单位: <el-input v-model="info.input5" style="padding: 0 1rem;width: 54.5%"></el-input>
+                  </el-col>
+                  <el-col :span="10">
+                    组织机构代码: <el-input v-model="info.input6" style="padding: 0 0.12rem;width: 50%"></el-input>
+                    <el-button icon="el-icon-search" circle></el-button>
+                  </el-col>
+                </el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 8.28rem;">
+                    单位行业: <el-select v-model="info.value1" style="padding: 0 1rem;width: 54.5%"></el-select>
+                  </el-col>
+                  <el-col :span="10">单位性质: <el-select v-model="info.value2" style="padding: 0 1.8rem;width: 50.5%"></el-select> </el-col>
+                </el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 8.28rem;">
+                    职位类别: <el-select v-model="info.value3" style="padding: 0 1rem;width: 54.5%"></el-select>
+                  </el-col>
+                  <el-col :span="10">身份证号码: <el-input v-model="info.input7" style="padding: 0 1rem;width: 50%"></el-input> </el-col>
+                </el-row>
+                <el-row class="titleStyle pUD04">
+                  <el-col :span="14" style="padding: 0 5.6rem;">
+                    单位联系人电话: <el-input v-model="info.input8" style="padding: 0 1rem;width: 47.8%"></el-input>
+                  </el-col>
+                  <el-col :span="10">实习途径: <el-select v-model="info.value4" style="padding: 0 1.8rem;width: 50.5%"></el-select> </el-col>
+                </el-row>
+                <el-row class="titleStyle pUD04" style="padding-left:8.3rem">
+                  实习地点:<el-input v-model="info.input9" style="padding: 0 1.25rem;width: 78.3%"></el-input>
+                </el-row>
+                <el-row class="titleStyle fontColor pUD04">
+                  实习合同
+                </el-row>
+                <el-row class="pUD04">
+                  <uploadFile ref="uploadFile"></uploadFile>
+                </el-row>
+              </el-form-item>
+            </el-form>
+            <el-row type="flex" justify="center">
+              <el-col :span="1">
+                <el-button type="success">提交</el-button>
+              </el-col>
+            </el-row>
+          </el-tab-pane>
+        </el-tabs>
+      </template>
+    </list-normal>
+  </div>
+</template>
+
+<script>
+import uploadFile from '@/components/upload-file.vue';
+import listNormal from '@/layout/list-normal.vue';
+import _ from 'lodash';
+import { mapActions, mapState } from 'vuex';
+export default {
+  name: 'index',
+  components: {
+    listNormal,
+    uploadFile,
+  },
+  data: () => ({
+    dataList: [{ score: '成绩', practice: '-', record: '-', plan: '-', search: '-', skill: '-' }],
+    activeName: '1',
+    info: {},
+    select: '',
+    circleUrl: '',
+    sizeList: '',
+    input1: '',
+    input2: '',
+    input3: '',
+    input4: '',
+    input5: '',
+    input6: '',
+    input7: '',
+    input8: '',
+    input9: '',
+    value1: [],
+    value2: [],
+    value3: [],
+    value4: [],
+    radio: '',
+    listSelect: [],
+    currentPage: 1,
+    totalRow: 1,
+  }),
+  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;
+}
+.titleStyle {
+  border-bottom-style: dashed;
+  border-width: 1px;
+  border-color: #cccccc;
+}
+.fontColor {
+  font-size: 1.5rem;
+  color: green;
+}
+.pUD04 {
+  padding: 0.4rem 0;
+}
+</style>

+ 807 - 0
src/views/want/resume/index.vue

@@ -0,0 +1,807 @@
+<template>
+  <div id="resume">
+    <detail-layout>
+      <template v-slot:title>
+        我的简历
+      </template>
+      <template #main v-if="loading">
+        <el-row>
+          <el-form :model="info">
+            <el-card class="box-card" shadow="never">
+              <template #header>
+                <el-row type="flex" align="middle" justify="end">
+                  <!-- <el-col :span="18">求职意向</el-col> -->
+                  <el-col :span="6" style="text-align:right;">
+                    <el-button type="success" size="small" icon="el-icon-view" @click="checkResume()">预览</el-button>
+                  </el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <form-item label="期望行业">
+                  <el-select v-model="expect.industry" :multiple="true" :filterable="true" placeholder="请选择" style="width: 100%">
+                    <el-option v-for="(item, index) in options" :key="index" :label="item.label" :value="item.label"> </el-option>
+                  </el-select>
+                </form-item>
+                <form-item label="期望职业"> <el-input v-model="expect.job" placeholder="请选择期望职业"></el-input></form-item>
+                <form-item label="职位分类">
+                  <tag-checkbox
+                    :max="3"
+                    placeholder="点击选择职位分类,最多选择3个"
+                    :firstList="jobTypeList"
+                    @selectChange="selectChange"
+                    @listChange="listChange"
+                    :selected="expect.category"
+                    type="category"
+                  ></tag-checkbox>
+                </form-item>
+                <form-item label="期望城市">
+                  <tag-all-select
+                    placeholder="点击选择城市"
+                    :firstList="provinceList"
+                    :secondList="cityList"
+                    @selectChange="selectChange"
+                    @listChange="listChange"
+                    :selected="expect.city"
+                    type="city"
+                  ></tag-all-select>
+                </form-item>
+                <form-item label="期望薪资">
+                  <template>
+                    <el-select v-model="expect.salary" placeholder="请选择..." style="width: 100%">
+                      <el-option v-for="(item, index) in options4" :key="index" :label="item.label" :value="item.label"> </el-option>
+                    </el-select>
+                  </template>
+                </form-item>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="18">基本信息</el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <form-item label="头像">
+                  <upload
+                    :limit="1"
+                    :data="info.avatar_url"
+                    type="avatar_url"
+                    :url="`/files/stud/resume_avatar/${user.id}/upload`"
+                    @upload="uploadSuccess"
+                  ></upload>
+                </form-item>
+              </el-row>
+              <el-row>
+                <form-item label="姓名"> <el-input v-model="info.info.xm" placeholder="请输入姓名" readonly></el-input></form-item>
+                <form-item label="性别">
+                  <el-radio-group v-model="info.info.xb" disabled>
+                    <el-radio label="男"></el-radio>
+                    <el-radio label="女"></el-radio>
+                  </el-radio-group>
+                </form-item>
+                <!-- <form-item label="籍贯"> <el-input v-model="info.native" placeholder="请输入籍贯"></el-input></form-item> -->
+                <form-item label="政治面貌">
+                  <el-select v-model="info.info.zzmm" placeholder="请选择..." style="width: 100%">
+                    <el-option v-for="(item, index) in options5" :key="index" :label="item.label" :value="item.label"> </el-option>
+                  </el-select>
+                </form-item>
+                <form-item label="学历"> <el-input v-model="info.info.xl" placeholder="请输入学历" readonly></el-input></form-item>
+                <form-item label="学校"> <el-input v-model="info.info.yx" placeholder="请输入院校" readonly></el-input></form-item>
+                <form-item label="所学专业"> <el-input v-model="info.info.zy" placeholder="请输入所学专业" readonly></el-input></form-item>
+                <form-item label="生源地"> <el-input v-model="info.info.syszd" placeholder="请输入学生生源地" readonly></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.contact.email" placeholder="请输入邮箱"></el-input></form-item>
+                <!-- <form-item label="学历">
+                  <template>
+                    <el-select v-model="info.value6" placeholder="请选择..." style="width: 100%">
+                      <el-option v-for="(item, index) in options6" :key="index" :label="item.label" :value="item.value"> </el-option>
+                    </el-select>
+                  </template>
+                </form-item> -->
+                <!-- <form-item label="手机"> <el-input v-model="info.phone" placeholder=" "></el-input></form-item>
+                <form-item label="邮箱"> <el-input v-model="info.email" placeholder=" "></el-input></form-item> -->
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row>
+                  <el-col :span="18">自我介绍</el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <form-item label="一句话描述"> <el-input v-model="info.profile" placeholder="请输入一句话描述"></el-input></form-item>
+                <form-item label="专业技能"> <el-input v-model="info.skill" placeholder="请输入专业技能"></el-input></form-item>
+                <el-row>
+                  <form-item label="个人介绍">
+                    <el-input type="textarea" :rows="8" placeholder="请输入内容" v-model="info.content" maxlength="100" show-word-limit> </el-input>
+                  </form-item>
+                </el-row>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">教育经历</el-col>
+                  <el-col :span="3"><el-button type="success" size="mini" icon="el-icon-plus" @click="educationsDialog = true">新增</el-button></el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <template>
+                  <el-table :data="info.educations" style="width: 100%">
+                    <el-table-column prop="yx" align="left" label="毕业院校"> </el-table-column>
+                    <el-table-column prop="fy" align="left" label="院系/分院"> </el-table-column>
+                    <el-table-column prop="zy" align="center" label="专业"> </el-table-column>
+                    <el-table-column prop="xl" align="center" label="学历"> </el-table-column>
+                    <el-table-column prop="rxsj" align="center" label="就读时间"> </el-table-column>
+                    <el-table-column prop="bysj" align="center" label="毕业时间"> </el-table-column>
+                    <el-table-column align="center" label="操作">
+                      <template v-slot="scope">
+                        <el-button type="text" @click="openEdit('educations', scope.row, scope.$index)">编辑</el-button>
+                        <el-button type="text" @click="subListDelete('educations', scope.$index)">删除</el-button>
+                      </template>
+                    </el-table-column>
+                  </el-table>
+                </template>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">工作或项目经历</el-col>
+                  <el-col :span="3"><el-button type="success" size="mini" icon="el-icon-plus" @click="worksDialog = true">新增</el-button></el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <template>
+                  <el-table :data="info.works" style="width: 100%">
+                    <el-table-column prop="corpname" align="left" label="公司/项目"> </el-table-column>
+                    <el-table-column prop="position" align="center" label="职位/角色"> </el-table-column>
+                    <el-table-column prop="begin" align="center" label="开始时间"> </el-table-column>
+                    <el-table-column prop="end" align="center" label="结束时间"> </el-table-column>
+                    <el-table-column align="center" label="操作">
+                      <template v-slot="scope">
+                        <el-button type="text" @click="openEdit('works', scope.row, scope.$index)">编辑</el-button>
+                        <el-button type="text" @click="subListDelete('works', scope.$index)">删除</el-button>
+                      </template>
+                    </el-table-column>
+                  </el-table>
+                </template>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">证书</el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <el-col :span="24">
+                  <upload-file
+                    :url="`/files/stud/resume_${user.id}_honors/upload`"
+                    desc="只能上传不超过2MB文件"
+                    :limit="100"
+                    @upload="uploadSuccess"
+                    @changeName="changeName"
+                    @toRemove="toRemove"
+                    type="honors"
+                    :data="uploads.honors"
+                  ></upload-file>
+                </el-col>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">能力介绍<span style="font-weight: 700; font-size: 0.75rem;">(如您的作品,成绩单,证书等证明材料)</span></el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <el-col :span="24">
+                  <upload-file
+                    :url="`/files/stud/resume_${user.id}abilities/upload`"
+                    desc="只能上传不超过2MB文件"
+                    :limit="100"
+                    @upload="uploadSuccess"
+                    @changeName="changeName"
+                    @toRemove="toRemove"
+                    type="abilities"
+                    :data="uploads.abilities"
+                  ></upload-file>
+                </el-col>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">个人风采</el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <el-col :span="24">
+                  <upload-file
+                    :url="`/files/stud/resume_${user.id}shows/upload`"
+                    desc="只能上传不超过2MB文件"
+                    :limit="100"
+                    @upload="uploadSuccess"
+                    @changeName="changeName"
+                    @toRemove="toRemove"
+                    type="shows"
+                    :data="uploads.shows"
+                  ></upload-file>
+                </el-col>
+              </el-row>
+            </el-card>
+            <el-card class="box-card" shadow="never" style="margin-top: 2rem">
+              <template #header>
+                <el-row type="flex" align="middle">
+                  <el-col :span="21">简历附件<span style="font-weight: 700; font-size: 0.75rem;">(可上传pdf、word文档或图片文件)</span></el-col>
+                </el-row>
+              </template>
+              <el-row>
+                <el-col :span="24">
+                  <upload-file
+                    :url="`/files/stud/resume_${user.id}attachments/upload`"
+                    desc="只能上传不超过2MB文件"
+                    :limit="100"
+                    @upload="uploadSuccess"
+                    @changeName="changeName"
+                    @toRemove="toRemove"
+                    type="attachments"
+                    :data="uploads.attachments"
+                  ></upload-file>
+                </el-col>
+              </el-row>
+            </el-card>
+          </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>
+    <el-dialog title="教育经历" center :visible.sync="educationsDialog">
+      <el-form :model="educationsForm" label-position="left" label-width="auto" ref="educationsForm" :rules="eduRules">
+        <el-form-item label="院校" prop="yx">
+          <el-input v-model="educationsForm.yx" placeholder="请输入院校"></el-input>
+        </el-form-item>
+        <el-form-item label="院系/分院" prop="fy">
+          <el-input v-model="educationsForm.fy" placeholder="请输入院系/分院"></el-input>
+        </el-form-item>
+        <el-form-item label="专业" prop="zy">
+          <el-input v-model="educationsForm.zy" placeholder="请输入专业"></el-input>
+        </el-form-item>
+        <el-form-item label="学历" prop="xl">
+          <el-input v-model="educationsForm.xl" placeholder="请输入学历"></el-input>
+        </el-form-item>
+        <el-form-item label="入学时间" prop="rxsj">
+          <el-date-picker
+            v-model="educationsForm.rxsj"
+            style="width:100%"
+            type="date"
+            placeholder="请选择入学时间"
+            format="yyyy-MM-dd"
+            value-format="yyyy-MM-dd"
+          >
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="毕业时间" prop="bysj">
+          <el-date-picker
+            v-model="educationsForm.bysj"
+            style="width:100%"
+            type="date"
+            placeholder="请选择毕业时间"
+            format="yyyy-MM-dd"
+            value-format="yyyy-MM-dd"
+          >
+          </el-date-picker>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="toClose('educations')">取 消</el-button>
+        <el-button type="primary" @click="toSubmitSub('educations')">保 存</el-button>
+      </template>
+    </el-dialog>
+    <el-dialog title="工作经历" center :visible.sync="worksDialog">
+      <el-form :model="worksForm" label-position="left" label-width="auto" ref="worksForm" :rules="worksRules">
+        <el-form-item label="公司名/项目名" prop="corpname">
+          <el-input v-model="worksForm.corpname" placeholder="请输入公司名/项目名"></el-input>
+        </el-form-item>
+        <el-form-item label="职位/角色" prop="position">
+          <el-input v-model="worksForm.position" placeholder="请输入职位/角色"></el-input>
+        </el-form-item>
+        <el-form-item label="工作描述" prop="desc">
+          <el-input v-model="worksForm.desc" type="textarea" autosize placeholder="请输入工作描述"></el-input>
+        </el-form-item>
+        <el-form-item label="开始时间" prop="begin">
+          <el-date-picker v-model="worksForm.begin" style="width:100%" type="date" placeholder="请选择开始时间" format="yyyy-MM-dd" value-format="yyyy-MM-dd">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="结束时间" prop="end">
+          <el-date-picker v-model="worksForm.end" style="width:100%" type="date" placeholder="请选择结束时间" format="yyyy-MM-dd" value-format="yyyy-MM-dd">
+          </el-date-picker>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="toClose('works')">取 消</el-button>
+        <el-button type="primary" @click="toSubmitSub('works')">保 存</el-button>
+      </template>
+    </el-dialog>
+    <el-dialog title="简历详情" center :visible.sync="resumeDialog" :fullscreen="true">
+      <resumes :info="preInfo"></resumes>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import resumes from '@resume/src/views/resume.vue';
+import uploadFile from '@/components/upload-file.vue';
+import upload from '@/components/upload.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';
+import _ from 'lodash';
+export default {
+  name: 'resume',
+  props: {
+    original: { type: Object, default: () => {} },
+  },
+  components: {
+    detailLayout,
+    formItem,
+    tagAllSelect,
+    upload,
+    uploadFile,
+    tagCheckbox,
+    resumes,
+  },
+  data: () => ({
+    loading: false,
+    educationsDialog: false,
+    resumeDialog: false,
+    worksDialog: false,
+    preInfo: {},
+    educationsForm: {},
+    worksForm: {},
+    uploads: {},
+    provinceList: [{ label: '北京市', value: '110000' }, { label: '吉林省', value: '220000' }, { label: '沈阳省', value: '210000' }],
+    cityList: [],
+    secondList: [{ label: '长春市', value: '220100' }, { label: '吉林市', value: '220200' }],
+    secondList1: [{ label: '沈阳市', value: '210100' }, { label: '大连市', value: '210200' }],
+    info: {
+      educations: [],
+      works: [],
+      honors: [],
+      abilities: [],
+      shows: [],
+      attachments: [],
+    },
+    expect: {},
+    eduRules: {
+      yx: [{ required: true, message: '请输入院校', trigger: 'blur' }],
+      fy: [{ required: true, message: '请输入院系/分院', trigger: 'blur' }],
+      zy: [{ required: true, message: '请输入专业', trigger: 'blur' }],
+      xl: [{ required: true, message: '请输入学历', trigger: 'blur' }],
+      rxsj: [{ required: true, message: '请选择入学时间', trigger: 'blur' }],
+      bysj: [{ required: true, message: '请选择毕业时间', trigger: 'blur' }],
+    },
+    worksRules: {
+      corpname: [{ required: true, message: '请输入公司名/项目名', trigger: 'blur' }],
+      position: [{ required: true, message: '请输入职位/角色', trigger: 'blur' }],
+      desc: [{ required: true, message: '请输入工作描述', trigger: 'blur' }],
+      begin: [{ required: true, message: '请选择开始时间', trigger: 'blur' }],
+      end: [{ required: true, message: '请选择结束时间', trigger: 'blur' }],
+    },
+    jobTypeList: [
+      {
+        label: 'IT',
+        value: '1',
+      },
+      {
+        label: 'test2',
+        value: '2',
+      },
+      {
+        label: 'test3',
+        value: '3',
+      },
+      {
+        label: 'test4',
+        value: '4',
+      },
+    ],
+    textarea: '',
+    options: [
+      {
+        value: '选项1',
+        label: '农、林、牧、渔业',
+      },
+      {
+        value: '选项2',
+        label: '采矿业',
+      },
+      {
+        value: '选项3',
+        label: '制造业',
+      },
+      {
+        value: '选项4',
+        label: '电力、热力、燃气及水生产和供应业',
+      },
+      {
+        value: '选项5',
+        label: '建筑业',
+      },
+    ],
+    options2: [
+      {
+        value: '选项1',
+        label: '产品',
+      },
+      {
+        value: '选项2',
+        label: '设计',
+      },
+      {
+        value: '选项3',
+        label: '编程/IT开发',
+      },
+      {
+        value: '选项4',
+        label: '测试',
+      },
+      {
+        value: '选项5',
+        label: 'IT运维',
+      },
+    ],
+    options3: [
+      {
+        label: '全国',
+        options3: [
+          {
+            value: 'Quanguo',
+            label: '全国',
+          },
+        ],
+      },
+      {
+        label: '直辖市',
+        options3: [
+          {
+            value: 'Beijing',
+            label: '成都',
+          },
+          {
+            value: 'Shanghai',
+            label: '上海',
+          },
+          {
+            value: 'Tianjin',
+            label: '天津',
+          },
+          {
+            value: 'Chongqin',
+            label: '重庆',
+          },
+        ],
+      },
+    ],
+    options4: [
+      {
+        value: '选项2',
+        label: '1K',
+      },
+      {
+        value: '选项3',
+        label: '2K',
+      },
+      {
+        value: '选项4',
+        label: '3K',
+      },
+      {
+        value: '选项5',
+        label: '4K',
+      },
+    ],
+    options5: [
+      {
+        value: '共青团员',
+        label: '共青团员',
+      },
+      {
+        value: '群众',
+        label: '群众',
+      },
+      {
+        value: '中共党员',
+        label: '中共党员',
+      },
+      {
+        value: '中共预备党员',
+        label: '中共预备党员',
+      },
+    ],
+    options6: [
+      {
+        value: '选项1',
+        label: '请选择...',
+      },
+      {
+        value: '选项2',
+        label: '博士',
+      },
+      {
+        value: '选项3',
+        label: '硕士',
+      },
+      {
+        value: '选项4',
+        label: '本科',
+      },
+      {
+        value: '选项5',
+        label: '大专',
+      },
+      {
+        value: '选项6',
+        label: '其他',
+      },
+    ],
+    form: {
+      name: '',
+      place: '',
+      num: '',
+    },
+    value1: [],
+    value2: [],
+    value3: [],
+    value4: [],
+    value5: [],
+    value6: [],
+  }),
+  created() {
+    this.search();
+  },
+  mounted() {
+    this.$nextTick(() => {
+      // this.$set(this, `info`, this.original);
+    });
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    ...mapActions(['userOperation', 'resumesOperation']),
+    async search() {
+      let result = await this.resumesOperation({ type: 'search', data: { id: this.user.id } });
+      this.$set(this, `expect`, result.data.expect);
+      this.returnData(result.data);
+      this.$set(this, `info`, result.data ? result.data : { info: {} });
+      result = await this.userOperation({ type: 'search', data: { id: this.user.id } });
+      this.$set(this.info, `info`, result.data.info ? result.data.info : { info: {} });
+      this.$set(this, `loading`, true);
+    },
+    async toSubmit() {
+      let data = JSON.parse(JSON.stringify(this.expect));
+      data = this.proData(data);
+      this.info.expect = data;
+      data = this.proPic(JSON.parse(JSON.stringify(this.info)));
+      let result;
+      if (this.info.id) {
+        result = await this.resumesOperation({ type: 'update', data: { info: data, id: this.info.id } });
+      } else {
+        result = await this.resumesOperation({ type: 'add', data: { info: data, schid: this.user.schid, studid: this.user.id } });
+      }
+      if (`${result.errcode}` === '0') {
+        this.$message.success('操作成功');
+      } else {
+        this.$message.error(result.errmsg);
+      }
+    },
+
+    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);
+      }
+    },
+    selectChange({ val, type }) {
+      //此方法是同步选择
+      if (type === 'category' || type === 'city' || type === 'salary') {
+        this.$set(this.expect, type, val);
+      } else {
+        this.$set(this.info, type, val);
+      }
+    },
+    toSubmitSub(type) {
+      this.$refs[`${type}Form`].validate(valid => {
+        if (valid) {
+          this.subListAdd(type);
+        }
+        return false;
+      });
+    },
+    subListAdd(type) {
+      let data = _.get(this, `${type}Form`);
+      if (data.index !== undefined) {
+        let { index, ...info } = data;
+        this.$set(_.get(this.info, `${type}`), `${index}`, info);
+        this.toClose(type);
+        return;
+      }
+      let list = _.get(this.info, `${type}`);
+      if (list) {
+        list.push(data);
+        this.$set(this.info, `${type}`, list);
+      } else {
+        list = [data];
+        this.$set(this.info, `${type}`, list);
+      }
+      this.toClose(type);
+    },
+    toClose(type) {
+      this.$set(this, `${type}Form`, {});
+      this.$set(this, `${type}Dialog`, false);
+    },
+    openEdit(type, info, index) {
+      let data = JSON.parse(JSON.stringify(info));
+      data.index = index;
+      this.$set(this, `${type}Form`, data);
+      this.$set(this, `${type}Dialog`, true);
+    },
+    subListDelete(type, index) {
+      let list = _.get(this.info, `${type}`);
+      list.splice(index, 1);
+      this.$set(this.info, `${type}`, list);
+    },
+    uploadSuccess({ type, data }) {
+      if (type !== 'avatar_url') {
+        let arr = _.get(this.uploads, type);
+        if (arr !== undefined) {
+          this.uploads[type].push({ 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);
+      }
+    },
+    toRemove({ type, data }) {
+      if (type !== 'avatar_url') {
+        let arr = _.get(this.uploads, type);
+        let newArr = arr.filter(item => item.uri !== data.url);
+        this.$set(this.uploads, `${type}`, newArr);
+        this.toSubmit();
+      }
+    },
+    proData(data) {
+      let mid = '';
+      data.industry.map(item => {
+        if (mid === '') {
+          mid = item;
+        } else {
+          mid += `,${item}`;
+        }
+      });
+      data.industry = mid;
+      mid = '';
+      data.city.map(item => {
+        if (mid === '') {
+          mid = item.label;
+        } else {
+          mid += `,${item.label}`;
+        }
+      });
+      data.city = mid;
+      mid = '';
+      data.category.map(item => {
+        if (mid === '') {
+          mid = item.label;
+        } else {
+          mid += `,${item.label}`;
+        }
+      });
+      data.category = mid;
+      return data;
+    },
+    proPic(data) {
+      data.honors = _.get(this.uploads, `honors`) === undefined ? [] : _.get(this.uploads, `honors`);
+      data.abilities = _.get(this.uploads, `abilities`) === undefined ? [] : _.get(this.uploads, `abilities`);
+      data.shows = _.get(this.uploads, `shows`) === undefined ? [] : _.get(this.uploads, `shows`);
+      data.attachments = _.get(this.uploads, `attachments`) === undefined ? [] : _.get(this.uploads, `attachments`);
+      console.log(data);
+      return data;
+    },
+    returnData(data) {
+      if (data.expect.industry) {
+        let ind = data.expect.industry.split(',');
+        this.$set(this.expect, `industry`, ind);
+      }
+      if (data.expect.city) {
+        let ind = data.expect.city.split(',');
+        let selected = [];
+        for (const select of ind) {
+          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];
+              }
+            }
+          }
+        }
+        this.$set(this.expect, `city`, selected);
+      }
+      if (data.expect.category) {
+        let ind = data.expect.category.split(',');
+        let arr = [];
+        for (const select of ind) {
+          let result = this.jobTypeList.filter(item => item.label === select);
+          arr = [...arr, ...result];
+        }
+        this.$set(this.expect, `category`, arr);
+      }
+      if (data.expect.salary) {
+        this.$set(this.expect, `salary`, data.expect.salary);
+      }
+      if (data.honors.length > 0) {
+        this.$set(this.uploads, `honors`, data.honors);
+      }
+      if (data.abilities.length > 0) {
+        this.$set(this.uploads, `abilities`, data.abilities);
+      }
+      if (data.shows.length > 0) {
+        this.$set(this.uploads, `shows`, data.shows);
+      }
+      if (data.attachments.length > 0) {
+        this.$set(this.uploads, `attachments`, data.attachments);
+      }
+    },
+    checkResume() {
+      let preInfo = JSON.parse(JSON.stringify(this.info));
+      let data = JSON.parse(JSON.stringify(this.expect));
+      data = this.proData(data);
+      preInfo.expect = data;
+      preInfo = this.proPic(preInfo);
+      this.$set(this, `preInfo`, preInfo);
+      this.resumeDialog = true;
+    },
+    changeName({ type, data }) {
+      let newObject = { name: data.name, uri: data.url };
+      let list = _.get(this.uploads, type);
+      if (list.length > 0) {
+        let index = _.findIndex(list, item => {
+          return item.uri === data.url;
+        });
+        this.$set(list, `${index}`, newObject);
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.tip {
+  color: #999999;
+}
+.rowStyle {
+  border-bottom: 1px dashed;
+}
+</style>

+ 68 - 0
vue.config.js

@@ -0,0 +1,68 @@
+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: '8002',
+    //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,
+      },
+      '/loginProject': {
+        target: 'http://localhost:8001',
+        changeOrigin: true,
+        ws: true,
+      },
+      '/studentProject': {
+        target: 'http://localhost:8002',
+        changeOrigin: true,
+        ws: true,
+      },
+      '/corpProject': {
+        target: 'http://localhost:8003',
+        changeOrigin: true,
+        ws: true,
+      },
+      '/protalProject': {
+        target: 'http://localhost:8004',
+        changeOrigin: true,
+        ws: true,
+      },
+      '/mobileProject': {
+        target: 'http://localhost:8005',
+        changeOrigin: true,
+        ws: true,
+      },
+    },
+  },
+};