【项目实训】前端对话界面设计

1. 设计思路

目标效果:

这个页面分为多个区域:导航栏,条件选择区域,信息展示区域,对话区域

2. 项目结构

项目使用vue搭建:

在home.vue中就可以清楚地看出vue组件之间的嵌套关系

导航栏在组件nav.vue中编写,剩余部分都是view。

3. 代码编写

3.1 导航栏

代码如下

<template>
  <div class="nav">
    <div class="nav-menu-wrapper">
      <ul class="menu-list">
        <li
            v-for="(item, index) in menuList"
            :key="index"
            :class="{ activeNav: index === current }"
            @click="changeMenu(index)"
        >
          <div class="block"></div>
          <span class="iconfont" :class="item"></span>
        </li>
      </ul>
    </div>
    <div class="own-pic">
      <HeadPortrait :imgUrl="imgUrl" ></HeadPortrait>
    </div>
  </div>
</template>

<script>
import HeadPortrait from "./HeadPortrait.vue";

export default {
  components: {
    HeadPortrait,
  },
  data() {
    return {
      menuList: [
        "icon-xinxi",
        "icon-shipin",
        "icon-shu",
        "icon-shandian",
        "icon-shezhi",
      ],
      current: 0,
      imgUrl: require("@/assets/img/robot.jpg"),
    };
  },
  methods: {
    changeMenu(index) {
      switch (index) {
        case 0:
          this.$router.push(
              {
                name: "ChatHome",
              },
              () => {
              }
          );
          break;
        case 1:
          this.$router.push(
              {
                name: "Experience",
              },
              () => {
              }
          );
          break;
        case 2:
          this.$router.push(
              {
                name: "Company",
              },
              () => {
              }
          );
          break;
        case 3:
          this.$router.push(
              {
                name: "InterviewExperience",
              },
              () => {
              }
          );
          break;
        case 4:
          this.$message("该功能还没有开发哦,敬请期待一下吧~🥳");
          break;
        default:
          this.$router.push({
            name: "ChatHome",
          });
      }

      this.current = index;
    },
  },
};
</script>

<style lang="scss" scoped>
.nav {
  width: 100%;
  height: 90vh;
  position: relative;
  border-radius: 20px 0 0 20px;

  .nav-menu-wrapper {
    position: absolute;
    top: 40%;
    transform: translate(0, -50%);

    .menu-list {
      margin-left: 10px;

      li {
        margin: 40px 0 0 30px;
        list-style: none;
        cursor: pointer;
        position: relative;

        .block {
          background-color: rgb(29, 144, 245);
          position: absolute;
          left: -40px;
          width: 6px;
          height: 25px;
          transition: 0.5s;
          border-top-right-radius: 4px;
          border-bottom-right-radius: 4px;
          opacity: 0;
        }

        &:hover {
          span {
            color: rgb(29, 144, 245);
          }

          .block {
            opacity: 1;
          }
        }
      }
    }
  }

  .own-pic {
    position: absolute;
    bottom: 10%;
    margin-left: 25px;
  }
}

.activeNav {
  span {
    color: rgb(29, 144, 245);
  }

  .block {
    opacity: 1 !important;
  }
}
</style>

3.2 对话View

对话View是一个chatHome的View,里面包括了index和chatWindow两个部分。

index就是上面展示的选择条件的区域,chatWindow则是剩余的区域。

index.vue

<template>
  <div v-loading="isLoading" element-loading-text="加载中" element-loading-background="rgba(180, 180, 180, 0.8)"
       class="chatHome">
    <div class="chatLeft">
      <div class="title">
        <h1>职跃助手</h1>
      </div>

      <el-form class="user-settings" ref="form" :model="form" label-width="80px" label-position="top">
        <el-form-item class="settings-unit">
          <span class="settings-text">选择岗位</span>
          <el-select class="my-selector" v-model="form.job" placeholder="请选择岗位">
            <el-option
                v-for="item in jobOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            />
          </el-select>
        </el-form-item>

        <el-form-item class="settings-unit">
          <span class="settings-text">选择公司</span>
          <el-select class="my-selector" v-model="form.company" placeholder="请选择公司">
            <el-option
                v-for="item in companyOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            />
          </el-select>
        </el-form-item>

        <el-form-item class="settings-unit">
          <span class="settings-text">选择面试类型</span>
          <el-select class="my-selector" v-model="form.interview" placeholder="请选择面试类型">
            <el-option
                v-for="item in interviewOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            />
          </el-select>
        </el-form-item>

        <el-form-item class="settings-unit">
          <div class="flex-container">
            <span class="settings-text">上传你的简历</span>
            <div class="my-selector">
              <input type="file" style="color: white" @change="onFileChange"/>
            </div>
          </div>
        </el-form-item>
      </el-form>
      <el-button type="primary" style="margin-left: 90px; margin-top: 40px" @click="onSubmit">上传文件</el-button>


    </div>

    <div class="chatRight">
      <div>
        <ChatWindow
            :frinedInfo="chatWindowInfo"
            :question="question"
            :startInterview="startInterview"
            :buttenText="buttonText"
            @personCardSort="personCardSort"
        ></ChatWindow>
      </div>
    </div>
  </div>
</template>

<script>
import PersonCard from "@/components/PersonCard.vue";
import ChatWindow from "./chatwindow.vue";

import {getFriend} from "@/api/getData";
import request from "@/utils/request";

export default {
  name: "App",
  components: {
    PersonCard,
    ChatWindow,
  },
  data() {
    return {
      question: [],
      pcCurrent: "",
      personList: [],
      startInterview: false,
      isLoading: false,
      buttonText: "请先输入个人信息,提交简历",
      chatWindowInfo: {},
      companyOptions: [
        {
          value: '华为',
          label: '华为'
        }, {
          value: '阿里',
          label: '阿里'
        }, {
          value: '腾讯',
          label: '腾讯'
        }],
      jobOptions: [
        {
          value: '前端',
          label: '前端'
        }, {
          value: '后端',
          label: '后端'
        }, {
          value: '移动',
          label: '移动'
        }],
      interviewOptions: [
        {
          value: '技术面',
          label: '技术面',
        },
        {
          value: 'HR面',
          label: 'HR面',
        }
      ],
      form: {
        job: '',
        company: '',
        interview: '',
      },
      file: null
    };
  },
  mounted() {
    getFriend().then((res) => {
      console.log(res);
      this.personList = res;
    });
    this.clickPerson({
      img: "",
      name: "机器人1号",
      detail: "我是机器人1号",
      lastMsg: "to do",
      id: "1002",
      headImg: require("@/assets/img/img_2.png"),
    },)
  },
  methods: {

    clickPerson(info) {
      this.chatWindowInfo = info;
      this.personInfo = info;
      this.pcCurrent = info.id;
    },

    personCardSort(id) {
      if (id !== this.personList[0].id) {
        console.log(id);
        let nowPersonInfo;
        for (let i = 0; i < this.personList.length; i++) {
          if (this.personList[i].id === id) {
            nowPersonInfo = this.personList[i];
            this.personList.splice(i, 1);
            break;
          }
        }
        this.personList.unshift(nowPersonInfo);
      }
    },
    onFileChange(e) {
      this.file = e.target.files[0];
      console.log(this.file)
    },
    onSubmit() {
      let formData = new FormData();
      // 添加文本字段
      for (let key in this.form) {
        if (this.form[key] === '') {
          alert("请填写完整信息");
          return;
        }
        formData.append(key, this.form[key]);
      }
      // 添加文件
      console.log(this.file)
      if (this.file) {
        formData.append('file', this.file);
      } else {
        alert("请上传简历文件!");
        return;
      }
      this.isLoading = true;
      // 使用axios或fetch发送POST请求
      // 假设你的Flask后端API地址是 '/upload'
      // delete formData.headers['Content-Type'];
      request.post('/upload_file', formData, {
        headers: {
          // 注意:当使用 FormData 时,不需要手动设置 Content-Type
          // axios 会自动处理
        },
      }).then(response => {
        if (response.code === 200) {
          this.$message("上传成功~🥳");
          console.log(response.result)
          this.question = response.result
          this.startInterview = true
        }
        this.isLoading = false;
      }).catch(error => {
        console.error(error);
        this.isLoading = false;
        // 处理错误
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.chatHome {
  // margin-top: 20px;
  display: flex;

  .chatLeft {
    width: 280px;

    .title {
      color: #fff;
      padding-left: 10px;
    }

    .user-settings {
      margin-top: 20px;

      .settings-unit {
        padding-left: 10px;
        margin-top: 0px;
      }

      .settings-text {
        padding-left: 10px;
        margin-top: 0px;
        color: rgb(176, 178, 189);
      }

      .my-selector {
        padding-left: 10px;
        margin-top: 0px;
      }
    }
  }

  .chatRight {
    flex: 1;
    padding-right: 30px;

    .showIcon {
      position: absolute;
      top: calc(50% - 150px); /*垂直居中 */
      left: calc(50% - 50px); /*水平居中 */
      .icon-snapchat {
        width: 300px;
        height: 300px;
        font-size: 300px;
        // color: rgb(28, 30, 44);
      }
    }
  }
}
</style>

chatWindow.vue

<template>
  <div v-loading="isLoading" element-loading-text="加载中" element-loading-background="rgba(180, 180, 180, 0.8)"
       class="chatHome">
    <div class="chatLeft">
      <div class="title">
        <h1>职跃助手</h1>
      </div>

      <el-form class="user-settings" ref="form" :model="form" label-width="80px" label-position="top">
        <el-form-item class="settings-unit">
          <span class="settings-text">选择岗位</span>
          <el-select class="my-selector" v-model="form.job" placeholder="请选择岗位">
            <el-option
                v-for="item in jobOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            />
          </el-select>
        </el-form-item>

        <el-form-item class="settings-unit">
          <span class="settings-text">选择公司</span>
          <el-select class="my-selector" v-model="form.company" placeholder="请选择公司">
            <el-option
                v-for="item in companyOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            />
          </el-select>
        </el-form-item>

        <el-form-item class="settings-unit">
          <span class="settings-text">选择面试类型</span>
          <el-select class="my-selector" v-model="form.interview" placeholder="请选择面试类型">
            <el-option
                v-for="item in interviewOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
            />
          </el-select>
        </el-form-item>

        <el-form-item class="settings-unit">
          <div class="flex-container">
            <span class="settings-text">上传你的简历</span>
            <div class="my-selector">
              <input type="file" style="color: white" @change="onFileChange"/>
            </div>
          </div>
        </el-form-item>
      </el-form>
      <el-button type="primary" style="margin-left: 90px; margin-top: 40px" @click="onSubmit">上传文件</el-button>


    </div>

    <div class="chatRight">
      <div>
        <ChatWindow
            :frinedInfo="chatWindowInfo"
            :question="question"
            :startInterview="startInterview"
            :buttenText="buttonText"
            @personCardSort="personCardSort"
        ></ChatWindow>
      </div>
    </div>
  </div>
</template>

<script>
import PersonCard from "@/components/PersonCard.vue";
import ChatWindow from "./chatwindow.vue";

import {getFriend} from "@/api/getData";
import request from "@/utils/request";

export default {
  name: "App",
  components: {
    PersonCard,
    ChatWindow,
  },
  data() {
    return {
      question: [],
      pcCurrent: "",
      personList: [],
      startInterview: false,
      isLoading: false,
      buttonText: "请先输入个人信息,提交简历",
      chatWindowInfo: {},
      companyOptions: [
        {
          value: '华为',
          label: '华为'
        }, {
          value: '阿里',
          label: '阿里'
        }, {
          value: '腾讯',
          label: '腾讯'
        }],
      jobOptions: [
        {
          value: '前端',
          label: '前端'
        }, {
          value: '后端',
          label: '后端'
        }, {
          value: '移动',
          label: '移动'
        }],
      interviewOptions: [
        {
          value: '技术面',
          label: '技术面',
        },
        {
          value: 'HR面',
          label: 'HR面',
        }
      ],
      form: {
        job: '',
        company: '',
        interview: '',
      },
      file: null
    };
  },
  mounted() {
    getFriend().then((res) => {
      console.log(res);
      this.personList = res;
    });
    this.clickPerson({
      img: "",
      name: "机器人1号",
      detail: "我是机器人1号",
      lastMsg: "to do",
      id: "1002",
      headImg: require("@/assets/img/img_2.png"),
    },)
  },
  methods: {

    clickPerson(info) {
      this.chatWindowInfo = info;
      this.personInfo = info;
      this.pcCurrent = info.id;
    },

    personCardSort(id) {
      if (id !== this.personList[0].id) {
        console.log(id);
        let nowPersonInfo;
        for (let i = 0; i < this.personList.length; i++) {
          if (this.personList[i].id === id) {
            nowPersonInfo = this.personList[i];
            this.personList.splice(i, 1);
            break;
          }
        }
        this.personList.unshift(nowPersonInfo);
      }
    },
    onFileChange(e) {
      this.file = e.target.files[0];
      console.log(this.file)
    },
    onSubmit() {
      let formData = new FormData();
      // 添加文本字段
      for (let key in this.form) {
        if (this.form[key] === '') {
          alert("请填写完整信息");
          return;
        }
        formData.append(key, this.form[key]);
      }
      // 添加文件
      console.log(this.file)
      if (this.file) {
        formData.append('file', this.file);
      } else {
        alert("请上传简历文件!");
        return;
      }
      this.isLoading = true;
      // 使用axios或fetch发送POST请求
      // 假设你的Flask后端API地址是 '/upload'
      // delete formData.headers['Content-Type'];
      request.post('/upload_file', formData, {
        headers: {
          // 注意:当使用 FormData 时,不需要手动设置 Content-Type
          // axios 会自动处理
        },
      }).then(response => {
        if (response.code === 200) {
          this.$message("上传成功~🥳");
          console.log(response.result)
          this.question = response.result
          this.startInterview = true
        }
        this.isLoading = false;
      }).catch(error => {
        console.error(error);
        this.isLoading = false;
        // 处理错误
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.chatHome {
  // margin-top: 20px;
  display: flex;

  .chatLeft {
    width: 280px;

    .title {
      color: #fff;
      padding-left: 10px;
    }

    .user-settings {
      margin-top: 20px;

      .settings-unit {
        padding-left: 10px;
        margin-top: 0px;
      }

      .settings-text {
        padding-left: 10px;
        margin-top: 0px;
        color: rgb(176, 178, 189);
      }

      .my-selector {
        padding-left: 10px;
        margin-top: 0px;
      }
    }
  }

  .chatRight {
    flex: 1;
    padding-right: 30px;

    .showIcon {
      position: absolute;
      top: calc(50% - 150px); /*垂直居中 */
      left: calc(50% - 50px); /*水平居中 */
      .icon-snapchat {
        width: 300px;
        height: 300px;
        font-size: 300px;
        // color: rgb(28, 30, 44);
      }
    }
  }
}
</style>

这样就完成了基本的页面布局编写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值