Commit 9369e86c authored by duanledexianxianxian's avatar duanledexianxianxian 😁

project init

parents
Pipeline #185 failed with stages
> 1%
last 2 versions
not dead
module.exports = {
root: true,
env: {
node: true
},
extends: ["plugin:vue/essential", "eslint:recommended", "@vue/prettier"],
parserOptions: {
parser: "babel-eslint"
},
rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
}
};
.DS_Store
node_modules
/dist
# 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?
node_modules
/yarn.lock
# kim-mobile-app
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
```
健康管理师 HealthManager health-manager
医院生期末考试 HospitalFinalExam hospital-final-exam
研究生考试 GraduateExam graduate-exam
住院医生规培 ResidentRegulation resident-regulation
职业医生考试 ProfessionalDoctorExam professional-doctor-exam
```
// 对于使用 babel7 的用户,可以在 babel.config.js 中配置
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
};
This diff is collapsed.
{
"name": "kim-mobile-app",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.19.2",
"core-js": "^3.6.4",
"vant": "^2.6.3",
"vue": "^2.6.11",
"vue-router": "^3.1.6",
"vuex": "^3.1.3"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.3.0",
"@vue/cli-plugin-eslint": "~4.3.0",
"@vue/cli-plugin-router": "~4.3.0",
"@vue/cli-plugin-vuex": "~4.3.0",
"@vue/cli-service": "~4.3.0",
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.0",
"babel-plugin-import": "^1.13.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-vue": "^6.2.2",
"less": "^3.0.4",
"less-loader": "^5.0.0",
"prettier": "^1.19.1",
"vue-template-compiler": "^2.6.11"
}
}
<!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><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> 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>
<template>
<div id="app">
<router-view v-if="!$route.meta.keepAlive"></router-view>
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
</div>
</template>
<template>
<div class="root">
<van-popup v-model="show">内容</van-popup>
</div>
</template>
<script>
export default {
}
</script>
<style lang="less" scoped>
@import "~@/themes/vars.less";
.root{
}
</style>
\ No newline at end of file
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import "@/themes/global.less";
// 注册vant组件
import {
Search,
List,
Icon,
Cell,
CellGroup,
loading,
Button,
Toast,
Tab,
Tabs,
Tabbar,
TabbarItem,
Col,
Row,Popup
} from "vant";
Vue.use(Popup);
Vue.use(Icon);
Vue.use(Cell);
Vue.use(CellGroup);
Vue.use(loading);
Vue.use(Button);
Vue.use(Toast);
Vue.use(Tabs);
Vue.use(Tab);
Vue.use(Tabbar);
Vue.use(TabbarItem);
Vue.use(Col);
Vue.use(Row);
Vue.use(Search);
Vue.use(List);
Vue.config.productionTip = false;
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
import Vue from "vue";
import VueRouter from "vue-router";
import Index from "../views/index";
import Exam from "../views/exam/index";
import Video from "../views/video/index";
import Profile from "../views/profile/index";
import hospitalFinalExam from "../views/exam/hospitalFinalExam/index";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "Index",
component: Index,
children:[
{
path: 'exam',
component: Exam
},
{
path: '/video',
component: Video
},
{
path: '/profile',
component: Profile
},
{
path: '/hospital-final-exam',
component: hospitalFinalExam,
},
]
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
export default router;
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {}
});
@import "~@/themes/vars.less";
html,
body,
#app {
height: 100%;
background-color: #fff;
}
.flex-space-around{
display: flex;
justify-content: space-around;
}
\ No newline at end of file
// 字号
@font-size-xlg: 22px; // 大标题
@font-size-lg: 29px; // 标题
@font-size-xmd: 16px; // 副标题
@font-size-md: 14px; // 主字体
@font-size-sm: 12px; // 说明 描述
@font-size-zs: 10px; // 说明 描述
// 字色
//3a73e9
//3367D6
@color-primary: #3367d6; // 主色
@color-primary-active: #3367d6; // 主色
@color-primary-disable: #bec5d4; // 主色
@color-primary-sup: #374583; // 图标色
@color-border: #e3e7ef; // 边框
@color-background: #FFFFFF; // 背景颜色
@color-success: #3cd99d; // 辅助色-成功
@color-error: #ff5c6a; // 辅助色-异常
@color-warning: #ffbc19; // 辅助色-警告
// 间距
@space-32: 32px;
@space-lg: 24px;
@space-xmd: 22px;
@space-md: 20px;
@space-sm: 16px;
@space-xs: 14px;
@space-xxs: 12px;
@space-xxxs: 8px;
@space-xxxxs: 4px;
@space-zero: 0px;
// 字号
@font-size-xlg: 22px; // 大标题
@font-size-lg: 29px; // 标题
@font-size-xmd: 16px; // 副标题
@font-size-md: 14px; // 主字体
@font-size-sm: 12px; // 说明 描述
@font-size-zs: 10px; // 说明 描述
\ No newline at end of file
import axios from "axios";
class HttpRequest {
constructor(options) {
this.options = options;
this.queues = {};
}
getInsideConfig() {
let headers = {};
if (this.options.headers instanceof Function) {
headers = this.options.headers();
} else {
headers = { ...this.options.headers };
}
const config = {
baseURL: this.options.baseUrl, // baseURL
timeout: 30000,
responseType: "json",
withCredentials: false, // default
headers: {
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
Accept: "application/json",
"X-Requested-With": "XMLHttpRequest",
...headers,
},
};
return config;
}
destroy(url) {
delete this.queues[url];
}
interceptors(instance, url, options) {
// 请求拦截
instance.interceptors.request.use(
config => {
this.queues[url] = true;
if (options.beforeRequest instanceof Function) {
options.beforeRequest({ queues: this.queues, config, options });
}
return config;
},
error => Promise.reject(error)
);
// 响应拦截
instance.interceptors.response.use(
res => {
// success
this.destroy(url);
if (options.successHandler instanceof Function) {
options.successHandler({ queues: this.queues, res, options });
}
const { data, status } = res;
return { data, status };
},
error => {
// error
this.destroy(url);
if (options.errorHandler instanceof Function) {
options.errorHandler({ queues: this.queues, error, options });
}
return Promise.reject(error);
}
);
}
request(options) {
const instance = axios.create();
const ops = Object.assign(this.getInsideConfig(), options);
this.interceptors(instance, ops.url, ops);
return instance(ops);
}
}
export default HttpRequest;
import { Toast } from 'vant';
import HttpRequest from "./axios";
/**
* todo 还需处理国际化
*/
const codeMessage = {
200: "服务器成功返回请求的数据。",
201: "新建或修改数据成功。",
202: "一个请求已经进入后台排队(异步任务)。",
204: "删除数据成功。",
400: "发出的请求有错误,服务器没有进行新建或修改数据的操作。",
401: "用户没有权限(令牌、用户名、密码错误)。",
403: "用户得到授权,但是访问是被禁止的。",
404: "发出的请求针对的是不存在的记录,服务器没有进行操作。",
406: "请求的格式不可得。",
410: "请求的资源被永久删除,且不会再得到的。",
422: "当创建一个对象时,发生一个验证错误。",
500: "服务器发生错误,请检查服务器。",
502: "网关错误。",
503: "服务不可用,服务器暂时过载或维护。",
504: "网关超时。",
};
const {
request: {
baseUrl,
apiPrefix,
resCodeKey,
resMessageKey,
successCode,
isThrowError = true,
authCodes = ["error.system.authc"],
},
} = config;
/**
* 组装url
* @param {url} url
* @param {配置参数} more
*/
const merge = (url, more) => {
if (more && more.apiPrefix && typeof more.apiPrefix === "string") {
return `${config.apiPrefix}${url}`;
}
if (apiPrefix && typeof apiPrefix === "string") {
return `${config.apiPrefix}${url}`;
}
return url;
};
const headers = () => ({
Authorization: store.get("token"),
});
const axios = new HttpRequest({
baseUrl,
headers: headers || {},
});
/**
* 正常返回结果处理
* @param {返回请求数据} response
* @param {配置项} more
*/
const handleResponse = response => {
const { data } = response;
//
if (`${data[resCodeKey]}` !== `${successCode}`) {
if (isThrowError) {
const errorMessage = data[resMessageKey] || "后端接口返回异常";
let error = new Error();
error = { ...error, ...data };
error.code = data[resCodeKey];
error.message = errorMessage;
return Promise.reject(error);
}
}
// success
return data;
};
const handleError = error => {
const { response } = error;
// status
if (response && response.status) {
const errorText = codeMessage[response.status] || response.statusText;
const {
status,
config: { url },
} = response;
Dialog.alert({
title: '警告',
message: error.message
});
Toast(`请求错误 ${status} ${errorText}`);
if (status <= 504 && status >= 500) {
router.push("/exception/500");
}
// 跳转到登录页面 可能原因:token 失效
if (status === 403 && status === 401) {
clearLoginStatus();
router.push("/user/login");
}
} else {
// code
const { code, message } = error;
notification.error({
key: `notification_${code ? message : code}`,
message: "请求错误",
description: message,
});
// 跳转到登录页面 可能原因:token 失效
if (authCodes && authCodes.includes(code)) {
// 清空相关信息
clearLoginStatus();
router.push("/user/login");
}
}
if (isThrowError) {
throw error;
} else {
return error;
}
};
export default function request(url, options = {}, more = {}) {
let newOptions = options;
newOptions.url = url;
if (more.headers) {
newOptions = { ...options, headers: more.headers };
}
return axios
.request(newOptions)
.then(response => handleResponse(response, more))
.catch(error => handleError(error));
}
const get = (url, data, more = {}) =>
request(
`${merge(url, more)}`,
{
method: "get", // default
params: data,
},
more
);
const post = (url, data, more = {}) =>
request(
`${merge(url, more)}`,
{
method: "post", // default
data,
},
more
);
const put = (url, data, more = {}) =>
request(
`${merge(url, more)}`,
{
method: "put", // default
data,
},
more
);
const del = (url, data, more = {}) =>
request(
`${merge(url, more)}`,
{
method: "delete", // default
data,
},
more
);
const patch = (url, data, more = {}) =>
request(
`${merge(url, more)}`,
{
method: "patch", // default
data,
...more,
},
more
);
const formDataUpload = (url, options, more = {}) => {
const formData = new FormData();
if (options) {
Object.keys(options).forEach(key => {
formData.append(key, options[key]);
});
}
post(`${merge(url, more)}`, formData, {
headers: {
"Content-Type": "multipart/form-data",
Authorization: store.get("token"),
},
...more,
}).then(res => {
const { code } = res;
if (code === "sys.success" && typeof options.onSuccess) {
options.onSuccess(res);
} else {
return res;
}
});
};
/**
* 上传文件
* @param {url} url
* @param {data} data
* @param {type} type
* @param {more} more
*/
const uploadFile = (url, data, type = "formData", more = {}) => {
if (type === "formData") {
formDataUpload(url, data, more);
}
};
export { request, get, post, put, del, patch, uploadFile };
<template>
<div class="root">
<van-search v-model="value" placeholder="请输入搜索关键词" />
<div class="list">
<van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
<div class="item" v-for="(item,index) in list" :key="index">
<img src="@/assets/images/exam1.png" />
<div class="right">
<div class="title">{{item.title}}</div>
<div class="action">
<div class="like flex-space-around">
<van-icon name="like" size="24" :color="item.like===0? '#ff7b7b':'#cccccc'" />
<div class="label">收藏</div>
</div>
<div class="share flex-space-around">
<van-icon name="share" size="24" color="#cccccc" />
<div class="label">分享</div>
</div>
<div class="answer">
<van-button round type="info">开始答题</van-button>
</div>
</div>
</div>
</div>
</van-list>
</div>
</div>
</template>
<script>
export default {
data() {
return {
list: [],
loading: false,
finished: false
};
},
methods: {
onLoad() {
// 异步更新数据
// setTimeout 仅做示例,真实场景中一般为 ajax 请求
setTimeout(() => {
for (let i = 0; i < 10; i++) {
this.list.push({
id: i,
like: i % 3 === 0 ? 1 : 0,
title: "2020版中西医结合执业助理医师 模拟试"
});
}
// 加载状态结束
this.loading = false;
// 数据全部加载完成
if (this.list.length >= 40) {
this.finished = true;
}
}, 1000);
}
}
};
</script>
<style lang="less" scoped>
@import "~@/themes/vars.less";
.root {
/deep/ .van-icon {
line-height: 28px;
}
/deep/ .van-button {
line-height: 28px;
height: 28px;
}
.list {
.item {
font-size: @space-sm;
display: flex;
margin: 0 8px 24px;
justify-content: space-between;
img {
height: 92px;
width: 120px;
}
.right {
display: flex;
margin: 0 8px;
flex-direction: column;
justify-content: space-around;
.title {
margin-top: 2px;
color: #202030;
font-weight: 600;
}
.action {
margin-top: 8px;
display: flex;
justify-content: space-around;
.like {
line-height: 28px;
}
.share {
line-height: 28px;
}
.label {
padding-left: 8px;
font-size: 12px;
}
}
}
}
}
}
</style>
\ No newline at end of file
<template>
<div class="root">
<van-tabs v-model="active" :border="false" line-width='0'>
<van-tab title="基本题型">
<BasicList></BasicList>
</van-tab>
<van-tab title="重点题型">
<KeyList></KeyList>
</van-tab>
<van-tab title="模拟试卷">
<PracticeList></PracticeList>
</van-tab>
</van-tabs>
</div>
</template>
<script>
import BasicList from "./list/Basic";
import KeyList from "./list/Key";
import PracticeList from "./list/Practice";
export default {
components: {
BasicList,
KeyList,
PracticeList
},
data() {
return {
active: 2
};
}
};
</script>
<style lang="less" scoped>
@import "~@/themes/vars.less";
.root {
/deep/ .van-tab.van-tab--active {
.van-tab__text {
color: #176aff;
font-weight: 600;
font-size: 16px;
}
}
/deep/ .van-tab {
.van-tab__text {
font-weight: 300;
font-size: 14px;
color: #373129;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="root">
<List></List>
</div>
</template>
<script>
import List from "../components/List";
export default {
components: {
List
}
};
</script>
<style lang="less" scoped>
@import "~@/themes/vars.less";
.root {
}
</style>
\ No newline at end of file
<template>
<div class="root">
<List></List>
</div>
</template>
<script>
import List from "../components/List";
export default {
components: {
List
}
};
</script>
<style lang="less" scoped>
@import "~@/themes/vars.less";
.root {
}
</style>
\ No newline at end of file
<template>
<div class="root">
<List :dataSource={}></List>
</div>
</template>
<script>
import List from "../components/List";
export default {
components: {
List
},
};
</script>
<style lang="less" scoped>
@import "~@/themes/vars.less";
.root {
}
</style>
\ No newline at end of file
<template>
<div class="exam">
<router-view />
<!-- 头部 -->
<div class="header">
<div class="notice">
<div>
<img src="~@/assets/images/notice.png" />
</div>
<div class="title">
距离研究生考试还有
<span>{{day}}</span>天!
</div>
</div>
</div>
<!-- 菜单导航 -->
<div class="menus">
<!-- 两端对齐 -->
<van-row type="flex" justify="space-between">
<van-col span="12">
<div class="menu" @click="go('/hospital-final-exam')">
<img src="~@/assets/images/hospital-final-exam.png" />
<div class="title">医院生期末考试</div>
</div>
</van-col>
<van-col span="12">
<div class="menu" @click="go('/graduate-exam')">
<img src="~@/assets/images/graduate-exam.png" />
<div class="title">研究生考试</div>
</div>
</van-col>
</van-row>
<van-row type="flex" justify="space-between">
<van-col span="12">
<div class="menu" @click="go('/resident-regulation')">
<img src="~@/assets/images/resident-regulation.png" />
<div class="title">住院医生规培</div>
</div>
</van-col>
<van-col span="12">
<div class="menu" @click="go('/professional-doctor-exam')">
<img src="~@/assets/images/professional-doctor-exam.png" />
<div class="title">职业医生考试</div>
</div>
</van-col>
</van-row>
<van-row type="flex" justify="space-between">
<van-col span="12">
<div class="menu" @click="go('/health-manager')">
<img src="~@/assets/images/health-manager.png" />
<div class="title">健康管理师</div>
</div>
</van-col>
</van-row>
</div>
</div>
</template>
<script>
import {Toast} from 'vant'
export default {
data() {
return {
day: 234
};
},
methods: {
go: function(path) {
if (path === "/hospital-final-exam") {
this.$router.push(path);
} else {
Toast(`跳转到${path}`);
}
}
}
};
</script>
<style lang="less" scoped>
@import "~@/themes/vars.less";
.exam {
height: 100%;
background-color: #f0f2f5;
font-size: @font-size-md;
.header {
height: 217px;
background: url("/exam-header.png") center center no-repeat;
position: relative;
.notice {
position: absolute;
left: 5%;
right: 5%;
bottom: -22px;
line-height: 44px; /*让黄色div中的文字内容垂直居中*/
border-radius: 4px;
border: #176aff;
background-color: @color-background;
display: flex;
margin: @space-xxxs;
box-shadow: 0px 2px 12px 0px rgba(23, 106, 255, 0.16);
img {
margin-left: @space-sm;
vertical-align: middle;
line-height: 44px;
height: 28px;
width: 28px;
}
.title {
margin-left: @space-sm;
span {
color: #ffc541;
}
}
}
}
.menus {
overflow-y: auto;
max-width: calc(100vh - 217px);
padding: 32px 16px 0;
margin-bottom: 50px;
.menu {
text-align: center;
padding: 12px 0;
margin: 8px 8px;
background-color: #ebeef5;
img {
width: 48px;
height: 48px;
}
.title {
margin-top: 16px;
}
}
}
}
</style>
\ No newline at end of file
<template>
<div class="index">
<router-view />
<div class="conent">
<van-tabbar v-model="active" active-color="#1a6af9" inactive-color="#999999" route>
<van-tabbar-item v-for="(item,index) in getTabbarItems" :to='item.link' :icon="item.icon" :key='index'>{{item.label}}</van-tabbar-item>
</van-tabbar>
</div>
</div>
</template>
<script>
export default {
data() {
console.log(this.active);
return {
active: 0
};
},
computed: {
getTabbarItems: function() {
return [
{
link:'exam',
icon: this.active === 0 ? "./exam.png" : "./exam-inactive.png",
label: "首页"
},
{
link:'video',
icon: this.active === 1 ? "./video.png" : "./video-inactive.png",
label: "视频教程"
},
{
link:'profile',
icon: this.active === 2 ? "./profile.png" : "./profile-inactive.png",
label: "个人中心"
}
];
}
}
};
</script>
<style>
</style>
<style lang="less" scoped>
@import "~@/themes/vars.less";
.index{
height: 100%;
}
.conent {
color: red;
/deep/ .van-tabbar-item__icon {
margin-bottom: 0;
.van-icon {
img {
height: 28px;
width: 28px;
}
}
}
/deep/ .van-tabbar-item__text {
font-size: @font-size-zs;
}
}
</style>
\ No newline at end of file
<template>
<div>profile</div>
</template>
<style scoped>
</style>
\ No newline at end of file
<template>
<div>video</div>
</template>
<style scoped>
</style>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment