project init
This commit is contained in:
124
src/components/Tinymce/index.vue
Normal file
124
src/components/Tinymce/index.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<div class="tinymce-container editor-container">
|
||||
<textarea ref="text" :id="tinymceId" class="tinymce-textarea"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import plugins from './plugins'
|
||||
import toolbar from './toolbar'
|
||||
import {putFileFun} from '../../api/resource/oss.js'
|
||||
|
||||
let id = 0
|
||||
|
||||
function getid() {
|
||||
return `tinymce${id++}`
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'Tinymce',
|
||||
props: {
|
||||
height: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 360
|
||||
},
|
||||
value:{type:String,default:""}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ed:null,
|
||||
hasInit:false,
|
||||
tinymceId: getid(),
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initTinymce()
|
||||
},
|
||||
deactivated() {
|
||||
this.destroyTinymce()
|
||||
},
|
||||
destroyed() {
|
||||
this.ed.off('blur')
|
||||
this.destroyTinymce()
|
||||
},
|
||||
methods: {
|
||||
initTinymce() {
|
||||
window.tinymce.init({
|
||||
selector: `#${this.tinymceId}`,
|
||||
language:'zh_CN',
|
||||
plugins: plugins,
|
||||
toolbar: toolbar,
|
||||
height: this.height, //编辑器高度
|
||||
min_height: 400,
|
||||
fontsize_formats: '12px 14px 16px 18px 24px 36px 48px 56px 72px',
|
||||
font_formats: '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats;知乎配置=BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, WenQuanYi Micro Hei, sans-serif;小米配置=Helvetica Neue,Helvetica,Arial,Microsoft Yahei,Hiragino Sans GB,Heiti SC,WenQuanYi Micro Hei,sans-serif',
|
||||
extended_valid_elements:'script[src]',
|
||||
template_cdate_format: '[CDATE: %m/%d/%Y : %H:%M:%S]',
|
||||
template_mdate_format: '[MDATE: %m/%d/%Y : %H:%M:%S]',
|
||||
autosave_ask_before_unload: false,
|
||||
toolbar_mode : 'wrap',
|
||||
images_upload_base_path: '',
|
||||
images_upload_handler: function (blobInfo, succFun) {
|
||||
var file = blobInfo.file ? blobInfo.file : blobInfo.blob()
|
||||
putFileFun(file).then(e => {
|
||||
succFun(e.data.data.link)
|
||||
})
|
||||
},
|
||||
setup:(ed) => {
|
||||
this.ed = ed
|
||||
ed.on("blur", () => {
|
||||
this.$emit('input',this.getContent())
|
||||
})
|
||||
}
|
||||
}).then(()=>{
|
||||
this.hasInit = true
|
||||
this.setContent(this.value)
|
||||
});
|
||||
},
|
||||
destroyTinymce() {
|
||||
if (window.tinymce.get(this.tinymceId)) {
|
||||
window.tinymce.get(this.tinymceId).destroy()
|
||||
}
|
||||
},
|
||||
setContent(value) {
|
||||
if (this.hasInit)
|
||||
{window.tinyMCE.get(this.tinymceId).setContent(value)}
|
||||
},
|
||||
getContent() {
|
||||
return window.tinymce.get(this.tinymceId).getContent()
|
||||
},
|
||||
},
|
||||
watch:{
|
||||
value(val){
|
||||
this.setContent(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tinymce-container {
|
||||
position: relative;
|
||||
}
|
||||
.tinymce-container>>>.mce-fullscreen {
|
||||
z-index: 10000;
|
||||
}
|
||||
.tinymce-textarea {
|
||||
visibility: hidden;
|
||||
z-index: -1;
|
||||
}
|
||||
.editor-custom-btn-container {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
z-index: 2005;
|
||||
}
|
||||
.fullscreen .editor-custom-btn-container {
|
||||
z-index: 10000;
|
||||
position: fixed;
|
||||
}
|
||||
.editor-upload-btn {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
5
src/components/Tinymce/plugins.js
Normal file
5
src/components/Tinymce/plugins.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
const plugins = 'print preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount imagetools textpattern help emoticons autosave indent2em autoresize axupimgs'
|
||||
|
||||
|
||||
export default plugins
|
||||
6
src/components/Tinymce/toolbar.js
Normal file
6
src/components/Tinymce/toolbar.js
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
const toolbar = 'code undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | \
|
||||
styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | \
|
||||
table image media charmap emoticons hr pagebreak insertdatetime print preview | fullscreen | indent2em axupimgs'
|
||||
|
||||
export default toolbar
|
||||
126
src/components/basic-block/main.vue
Normal file
126
src/components/basic-block/main.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div class="basic-block"
|
||||
:style="styleName">
|
||||
<div class="box"
|
||||
:style="boxStyleName">
|
||||
<router-link :to="to">
|
||||
<span v-text="text"></span>
|
||||
<p v-text="dept"></p>
|
||||
<i :class="icon"></i>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'basicBlock',
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
},
|
||||
background: {
|
||||
type: String,
|
||||
},
|
||||
to: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
},
|
||||
dept: {
|
||||
type: String,
|
||||
},
|
||||
time: {
|
||||
type: [Number, String]
|
||||
},
|
||||
gutter: {
|
||||
type: [Number, String],
|
||||
default: 5,
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
},
|
||||
width: {
|
||||
type: [Number, String]
|
||||
},
|
||||
height: {
|
||||
type: [Number, String]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleName () {
|
||||
return {
|
||||
animationDelay: `${this.time / 25}s`,
|
||||
width: this.setPx(this.width),
|
||||
height: this.setPx(this.height),
|
||||
margin: this.setPx(this.gutter)
|
||||
}
|
||||
},
|
||||
boxStyleName () {
|
||||
return {
|
||||
backgroundColor: this.color,
|
||||
backgroundImage: `url('${this.background}')`,
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.basic-block {
|
||||
opacity: 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
animation: mymove 1s;
|
||||
animation-fill-mode: forwards;
|
||||
.box {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding: 15px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: all 1s;
|
||||
background-size: cover;
|
||||
&:hover {
|
||||
transform: rotateY(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
}
|
||||
span {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
}
|
||||
p {
|
||||
width: 80%;
|
||||
font-size: 10px;
|
||||
color: #eee;
|
||||
line-height: 22px;
|
||||
}
|
||||
i {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
right: 15px;
|
||||
font-size: 50px !important ;
|
||||
}
|
||||
@keyframes mymove {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
54
src/components/basic-container/main.vue
Normal file
54
src/components/basic-container/main.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="basic-container"
|
||||
:class="{'basic-container--block':block}">
|
||||
<el-card>
|
||||
<slot></slot>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "basicContainer",
|
||||
props: {
|
||||
radius: {
|
||||
type: [String, Number],
|
||||
default: 10
|
||||
},
|
||||
background: {
|
||||
type: String
|
||||
},
|
||||
block: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styleName () {
|
||||
return {
|
||||
borderRadius: this.setPx(this.radius),
|
||||
background: this.background,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.basic-container {
|
||||
padding: 10px 6px;
|
||||
box-sizing: border-box;
|
||||
&--block {
|
||||
height: 100%;
|
||||
.el-card {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.el-card {
|
||||
width: 100%;
|
||||
}
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
85
src/components/city-cascader/main.vue
Normal file
85
src/components/city-cascader/main.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<el-cascader
|
||||
:options="area.data"
|
||||
:show-all-levels="showAllLevels"
|
||||
v-model="model"
|
||||
:filterable="filterable"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
:clearable="clearable"
|
||||
></el-cascader>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
|
||||
function getProv(code) {
|
||||
return code.substring(0, 2) + "0000";
|
||||
}
|
||||
function getCity(code) {
|
||||
return code.substring(0, 4) + "00";
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: String,
|
||||
filterable: Boolean,
|
||||
placeholder: String,
|
||||
showAllLevels: { type: Boolean, default: true },
|
||||
disabled: Boolean,
|
||||
clearable: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch("InitArea");
|
||||
},
|
||||
watch: {},
|
||||
computed: {
|
||||
...mapGetters(["area"]),
|
||||
prov() {
|
||||
if (this.county) {
|
||||
return getProv(this.county);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
city() {
|
||||
if (this.county) {
|
||||
return getCity(this.county);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
county: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(val) {
|
||||
this.$emit("input", val);
|
||||
},
|
||||
},
|
||||
model: {
|
||||
set(val) {
|
||||
if (val.length > 2) {
|
||||
this.county = val[2];
|
||||
} else {
|
||||
this.county = undefined;
|
||||
}
|
||||
},
|
||||
get() {
|
||||
if (this.county) {
|
||||
return [this.prov, this.city, this.county];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
24
src/components/error-page/403.vue
Normal file
24
src/components/error-page/403.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="error-page">
|
||||
<div class="img"
|
||||
style=" background-image: url('./img/bg/403.svg');"></div>
|
||||
<div class="content">
|
||||
<h1>403</h1>
|
||||
<div class="desc">抱歉,你无权访问该页面</div>
|
||||
<div class="actions">
|
||||
<router-link :to="{path:'/'}">
|
||||
<el-button type="primary">返回首页</el-button>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "error-403"
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
</style>
|
||||
24
src/components/error-page/404.vue
Normal file
24
src/components/error-page/404.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="error-page">
|
||||
<div class="img"
|
||||
style=" background-image: url('./img/bg/404.svg');"></div>
|
||||
<div class="content">
|
||||
<h1>404</h1>
|
||||
<div class="desc">抱歉,你访问的页面不存在</div>
|
||||
<div class="actions">
|
||||
<router-link :to="{path:'/'}">
|
||||
<el-button type="primary">返回首页</el-button>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "error-404"
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
</style>
|
||||
24
src/components/error-page/500.vue
Normal file
24
src/components/error-page/500.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="error-page">
|
||||
<div class="img"
|
||||
style=" background-image: url('./img/bg/500.svg');"></div>
|
||||
<div class="content">
|
||||
<h1>500</h1>
|
||||
<div class="desc">抱歉,服务器出错了</div>
|
||||
<div class="actions">
|
||||
<router-link :to="{path:'/'}">
|
||||
<el-button type="primary">返回首页</el-button>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "error-500"
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./style.scss";
|
||||
</style>
|
||||
32
src/components/error-page/style.scss
Normal file
32
src/components/error-page/style.scss
Normal file
@@ -0,0 +1,32 @@
|
||||
.error-page {
|
||||
background: #f0f2f5;
|
||||
margin-top: -30px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.img {
|
||||
margin-right: 80px;
|
||||
height: 360px;
|
||||
width: 100%;
|
||||
max-width: 430px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
background-size: contain;
|
||||
}
|
||||
.content {
|
||||
h1 {
|
||||
color: #434e59;
|
||||
font-size: 72px;
|
||||
font-weight: 600;
|
||||
line-height: 72px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.desc {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/components/excel-import-upload/main.vue
Normal file
65
src/components/excel-import-upload/main.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<el-upload action="#" class="upload" drag :before-upload="beforeUpload">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处,或
|
||||
<em>点击上传</em>
|
||||
</div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<span>请上传 .xls,.xlsx 标准格式文件 </span>
|
||||
<span class="redFont"> 最多可上传1000条Excel数据</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isExcel } from "@/util/validate";
|
||||
import XLSX from "xlsx";
|
||||
|
||||
function fixdata(data) {
|
||||
let o = "";
|
||||
let l = 0;
|
||||
let w = 10240;
|
||||
for (; l < data.byteLength / w; ++l) {
|
||||
o += String.fromCharCode.apply(
|
||||
null,
|
||||
new Uint8Array(data.slice(l * w, l * w + w))
|
||||
);
|
||||
}
|
||||
o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)));
|
||||
return o;
|
||||
}
|
||||
|
||||
export default {
|
||||
props: { success: Function },
|
||||
methods: {
|
||||
beforeUpload(file) {
|
||||
if (!isExcel(file)) {
|
||||
this.$message.error("上传参保信息只能是 .xls,.xlsx 标准格式文件!");
|
||||
return;
|
||||
}
|
||||
let reader = new FileReader();
|
||||
let self = this;
|
||||
reader.onload = function(e) {
|
||||
var data = e.target.result;
|
||||
var wb = XLSX.read(btoa(fixdata(data)), {
|
||||
type: "base64"
|
||||
});
|
||||
var questions = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
self.success(questions);
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.upload {
|
||||
display: inline-block;
|
||||
}
|
||||
.redFont{
|
||||
color:#ff0000;
|
||||
}
|
||||
</style>
|
||||
21
src/components/go-back/main.vue
Normal file
21
src/components/go-back/main.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div style="text-align: right;margin: 0 0 5px 0">
|
||||
<el-button icon="el-icon-close" circle @click="goBack" size="small"></el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
goBack() {
|
||||
this.$router.go(-1);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
129
src/components/iframe/main.vue
Normal file
129
src/components/iframe/main.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<iframe :src="src"
|
||||
class="iframe"
|
||||
ref="iframe"></iframe>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from "vuex";
|
||||
import NProgress from "nprogress"; // progress bar
|
||||
import "nprogress/nprogress.css"; // progress bar style
|
||||
export default {
|
||||
name: "AvueIframe",
|
||||
data() {
|
||||
return {
|
||||
urlPath: this.getUrlPath() //iframe src 路径
|
||||
};
|
||||
},
|
||||
created() {
|
||||
NProgress.configure({showSpinner: false});
|
||||
},
|
||||
mounted() {
|
||||
this.load();
|
||||
this.resize();
|
||||
},
|
||||
props: ["routerPath"],
|
||||
watch: {
|
||||
$route: function () {
|
||||
this.load();
|
||||
},
|
||||
routerPath: function () {
|
||||
// 监听routerPath变化,改变src路径
|
||||
this.urlPath = this.getUrlPath();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["screen"]),
|
||||
src() {
|
||||
return this.$route.query.src
|
||||
? this.$route.query.src.replace("$", "#")
|
||||
: this.urlPath;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 显示等待框
|
||||
show() {
|
||||
NProgress.start();
|
||||
},
|
||||
// 隐藏等待狂
|
||||
hide() {
|
||||
NProgress.done();
|
||||
},
|
||||
// 加载浏览器窗口变化自适应
|
||||
resize() {
|
||||
window.onresize = () => {
|
||||
this.iframeInit();
|
||||
};
|
||||
},
|
||||
// 加载组件
|
||||
load() {
|
||||
this.show();
|
||||
var flag = true; //URL是否包含问号
|
||||
if (this.$route.query.src !== undefined && this.$route.query.src.indexOf("?") === -1) {
|
||||
flag = false;
|
||||
}
|
||||
var list = [];
|
||||
for (var key in this.$route.query) {
|
||||
if (key !== "src" && key !== "name" && key !== "i18n") {
|
||||
list.push(`${key}= this.$route.query[key]`);
|
||||
}
|
||||
}
|
||||
list = list.join("&").toString();
|
||||
if (flag) {
|
||||
this.$route.query.src = `${this.$route.query.src}${
|
||||
list.length > 0 ? `&list` : ""
|
||||
}`;
|
||||
} else {
|
||||
this.$route.query.src = `${this.$route.query.src}${
|
||||
list.length > 0 ? `?list` : ""
|
||||
}`;
|
||||
}
|
||||
//超时3s自动隐藏等待狂,加强用户体验
|
||||
let time = 3;
|
||||
const timeFunc = setInterval(() => {
|
||||
time--;
|
||||
if (time === 0) {
|
||||
this.hide();
|
||||
clearInterval(timeFunc);
|
||||
}
|
||||
}, 1000);
|
||||
this.iframeInit();
|
||||
},
|
||||
//iframe窗口初始化
|
||||
iframeInit() {
|
||||
const iframe = this.$refs.iframe;
|
||||
const clientHeight =
|
||||
document.documentElement.clientHeight - (screen > 1 ? 200 : 130);
|
||||
if (!iframe) return;
|
||||
iframe.style.height = `${clientHeight}px`;
|
||||
if (iframe.attachEvent) {
|
||||
iframe.attachEvent("onload", () => {
|
||||
this.hide();
|
||||
});
|
||||
} else {
|
||||
iframe.onload = () => {
|
||||
this.hide();
|
||||
};
|
||||
}
|
||||
},
|
||||
getUrlPath: function () {
|
||||
//获取 iframe src 路径
|
||||
let url = window.location.href;
|
||||
url = url.replace("/myiframe", "");
|
||||
return url;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
320
src/components/jl-help/index.vue
Normal file
320
src/components/jl-help/index.vue
Normal file
@@ -0,0 +1,320 @@
|
||||
<template>
|
||||
<!-- <div v-if="jlHelpShow" class="jl-help" :style="{'height': height, 'width': width +'px'}">-->
|
||||
<div class="jl-help avue-right-sidebar" :class="{'jl-help-active': jlHelpShow}">
|
||||
<div v-if="jlHelpShow">
|
||||
<div class="jl-switch">
|
||||
{{helpSwitch}}
|
||||
<el-switch
|
||||
v-model="switchValue"
|
||||
@change="switchHelp"
|
||||
active-text="默认弹出此侧边栏">
|
||||
</el-switch>
|
||||
</div>
|
||||
<!--政策法规 isShowPolicy -->
|
||||
<div v-if="helpList.isShowPolicy === 1">
|
||||
<div>
|
||||
<div class="big-blank"></div>
|
||||
<p class="jl-help-title">政策法规</p>
|
||||
<div class="medium-blank"></div>
|
||||
<div v-if="policyList.length === 0">
|
||||
暂无内容
|
||||
</div>
|
||||
<div class="jl-small-title" v-for="(item, index) in policyList" :key="index" @click="lookNews(item)">
|
||||
<span>[{{item.secondName}}]</span>
|
||||
<p>{{item.title}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="small-blank"></div>
|
||||
<div class="jl-hr"></div>
|
||||
</div>
|
||||
|
||||
<!--常见问题 isShowQuestion -->
|
||||
<div v-if="helpList.isShowQuestion === 1">
|
||||
<div>
|
||||
<div class="big-blank"></div>
|
||||
<p class="jl-help-title">常见问题</p>
|
||||
<div class="medium-blank"></div>
|
||||
<div v-if="questionList.length === 0">
|
||||
暂无内容
|
||||
</div>
|
||||
<div class="jl-small-title" v-for="(item, index) in questionList" :key="index" @click="lookNews(item)">
|
||||
<span>[{{item.secondName}}]</span>
|
||||
<p>{{item.title}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="small-blank"></div>
|
||||
<div class="jl-hr"></div>
|
||||
</div>
|
||||
|
||||
<!-- 广告位 isShowCarousel -->
|
||||
<div class="jl-advertisement" v-if="helpList.isShowCarousel === 1">
|
||||
<div class="big-blank"></div>
|
||||
<div>
|
||||
<img
|
||||
v-for="(item, index) in carouselVoList"
|
||||
:key="index"
|
||||
:src="item.image"
|
||||
class="img"
|
||||
alt="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 客服 isShowCustservice -->
|
||||
<div v-if="helpList.isShowCustservice === 1">
|
||||
<div class="big-blank"></div>
|
||||
<div class="jl-user">
|
||||
<div class="jl-head-portrait">
|
||||
<img src="../../../public/img/help/portrait.png" height="42" width="42"/>
|
||||
</div>
|
||||
<div class="jl-userInfo">
|
||||
<p class="name">您好,我是小助手</p>
|
||||
<p class="tip">您的专属客服顾问</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="jl-help-code">
|
||||
<div class="img"></div>
|
||||
<div class="jl-help-code-tips">
|
||||
扫码添加您的专属顾问
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="big-blank"></div>
|
||||
<div class="jl-hr"></div>
|
||||
</div>
|
||||
|
||||
<!--服务热线-->
|
||||
<div>
|
||||
<div class="big-blank"></div>
|
||||
<p class="jl-help-title">服务热线</p>
|
||||
<div class="medium-blank"></div>
|
||||
<div class="jl-help-phone">
|
||||
<img src="../../../public/img/help/phone.png" height="20" width="20"/>
|
||||
<p>185-0020-6848</p>
|
||||
</div>
|
||||
<div class="jl-help-time">
|
||||
<p>服务时间:周一至周六08:30-18:00</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: 120px;"></div>
|
||||
|
||||
</div>
|
||||
<el-drawer
|
||||
title="查看"
|
||||
:with-header="false"
|
||||
:visible.sync="drawer"
|
||||
:direction="direction"
|
||||
:size="drawerSize">
|
||||
<div v-html="content">
|
||||
</div>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState, mapGetters} from "vuex";
|
||||
import {
|
||||
getStore,
|
||||
} from '@/util/store'
|
||||
export default {
|
||||
name: "index",
|
||||
props: {
|
||||
helpShow: {
|
||||
default: true,
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
switchValue: true,
|
||||
drawer: false,
|
||||
direction: 'rtl',
|
||||
drawerSize: '1000px',
|
||||
content: '',
|
||||
height: 0,
|
||||
}
|
||||
},
|
||||
watch: {},
|
||||
methods: {
|
||||
// 每次点击时进行切换;修改状态在整个系统生效;
|
||||
switchHelp (v) {
|
||||
var helpSwitch = v ? 1 : 2
|
||||
this.$store.dispatch('jlHelpSwitch', helpSwitch)
|
||||
},
|
||||
lookNews(item){
|
||||
this.drawer = true
|
||||
this.content = item.content
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
helpTag: state => state.common.helpTag,
|
||||
helpList: state => state.common.helpList,
|
||||
}),
|
||||
jlHelpShow () {
|
||||
return this.helpTag
|
||||
},
|
||||
helpSwitch () {
|
||||
var helpSwitch = this.$store.state.common.helpSwitch
|
||||
var status = helpSwitch === 1 ? true : false;
|
||||
this.switchValue = status
|
||||
return ''
|
||||
},
|
||||
policyList () {
|
||||
return this.helpList.contentListPolicy
|
||||
},
|
||||
questionList () {
|
||||
return this.helpList.contentListQuestion
|
||||
},
|
||||
carouselVoList () {
|
||||
return this.helpList.carouselVoList
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
p {
|
||||
margin: 0!important;
|
||||
}
|
||||
.big-blank{
|
||||
height:24px;
|
||||
}
|
||||
.medium-blank{
|
||||
height:16px;
|
||||
}
|
||||
.small-blank{
|
||||
height:12px;
|
||||
}
|
||||
.jl-switch{
|
||||
height: 40px;border-bottom:1px solid #f6f6f6;
|
||||
margin-left: -20px;margin-right: -20px;line-height: 40px;padding-left:20px;
|
||||
}
|
||||
.jl-hr{
|
||||
width: 198px;
|
||||
height: 1px;
|
||||
background-color: rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.jl-help-active{
|
||||
border-top:1px solid #f6f6f6;
|
||||
border-left:1px solid #f6f6f6;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
|
||||
width: 240px !important;
|
||||
overflow: auto;min-height: 500px;
|
||||
height: calc(100% - 64px);
|
||||
background-color: #FFFFFF;
|
||||
padding: 0 20px 20px 20px;
|
||||
}
|
||||
.jl-help{
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.jl-help-title{
|
||||
font-weight: bold;
|
||||
|
||||
height: 19px;
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
.jl-small-title:hover p{
|
||||
color: rgba(0, 0, 0, 1) !important;
|
||||
}
|
||||
.jl-small-title:hover span{
|
||||
color: rgba(0, 0, 0, 1) !important;
|
||||
}
|
||||
.jl-small-title{
|
||||
display: flex;
|
||||
font-size: 12px;
|
||||
margin-bottom: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.jl-small-title span{
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
margin-right:12px;
|
||||
}
|
||||
.jl-small-title p{
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.jl-advertisement .img{
|
||||
width: 198px;
|
||||
height: 120px;
|
||||
background-color: #e3e3e3;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.jl-user{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
.jl-user .jl-head-portrait{
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #fdfef8;
|
||||
text-align: center;
|
||||
line-height: 44px;
|
||||
background-color: #fdfef8;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.jl-userInfo{
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.jl-userInfo .name{
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
.jl-userInfo .tip{
|
||||
color: #419EFF;
|
||||
}
|
||||
|
||||
.jl-help-code{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-flow: column;
|
||||
}
|
||||
.jl-help-code .img{
|
||||
background: url("/manage/img/kefu.jpg") no-repeat;
|
||||
background-size: 126px 126px;
|
||||
width: 126px;
|
||||
height: 126px;
|
||||
}
|
||||
.jl-help-code-tips{
|
||||
width: 120px;
|
||||
height: 17px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
line-height: 17px;
|
||||
/*margin-bottom: 20px;*/
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.jl-help-phone{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
.jl-help-phone img{
|
||||
margin-right: 14px;
|
||||
}
|
||||
|
||||
.jl-help-time{
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
line-height: 16px;
|
||||
margin-top: 9px;
|
||||
}
|
||||
|
||||
</style>
|
||||
151
src/components/jl-tab/main.vue
Normal file
151
src/components/jl-tab/main.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="jl-tabs" ref="body">
|
||||
<span
|
||||
v-for="(value, index) in data"
|
||||
ref="tab"
|
||||
:key="index"
|
||||
:class="{ 'jl-tab': true, active: active === index }"
|
||||
@click="change(index, $event)"
|
||||
>{{ value }}</span
|
||||
>
|
||||
<div :class="lineClass" :style="lineStyle"></div>
|
||||
</div>
|
||||
<div class="jl-tabs float" v-show="fixedShow">
|
||||
<span
|
||||
v-for="(value, index) in data"
|
||||
:key="index"
|
||||
class="jl-tab"
|
||||
:class="{ 'jl-tab': true, active: active === index }"
|
||||
@click="change(index, $event)"
|
||||
ref="tab"
|
||||
>{{ value }}</span
|
||||
>
|
||||
<div :class="lineClass" :style="lineStyle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
fixedShow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
maxFixed: { type: Number },
|
||||
data: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
active: 0,
|
||||
lineWidth: 0,
|
||||
lineLeft: 0,
|
||||
mounted: false,
|
||||
activeStatus: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.initLine();
|
||||
},
|
||||
computed: {
|
||||
lineStyle() {
|
||||
return { width: `${this.lineWidth}px`, left: `${this.lineLeft}px` };
|
||||
},
|
||||
lineClass() {
|
||||
return {
|
||||
line: true,
|
||||
mounted: this.mounted,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initLine() {
|
||||
if (this.data.length > 0) {
|
||||
this.$nextTick(() => {
|
||||
this.lineWidth = this.$refs.tab[0].clientWidth;
|
||||
this.lineLeft = this.$refs.tab[0].offsetLeft;
|
||||
setTimeout(() => {
|
||||
this.mounted = true;
|
||||
}, 20);
|
||||
});
|
||||
}
|
||||
},
|
||||
change(val) {
|
||||
if (val === this.active) {
|
||||
return;
|
||||
}
|
||||
this.active = val;
|
||||
this.activeStatus = true
|
||||
setTimeout(() => {
|
||||
this.activeStatus = false
|
||||
}, 500)
|
||||
this.$emit("change", this.data[val]);
|
||||
if (this.fixed && this.fixedShow) {
|
||||
this.$refs.body.scrollIntoView(true);
|
||||
}
|
||||
},
|
||||
tabNum(index){
|
||||
if(this.active === index)return
|
||||
this.active = index;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data() {
|
||||
this.initLine();
|
||||
},
|
||||
active(val){
|
||||
const tab = this.$refs.tab[val]
|
||||
this.lineWidth = tab.clientWidth;
|
||||
this.lineLeft = tab.offsetLeft;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.jl-tabs {
|
||||
width: 1000px;
|
||||
background-color: #fff;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
.jl-tab {
|
||||
padding: 20px 21px 18px 21px;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
letter-spacing: 0;
|
||||
text-align: center;
|
||||
color: #333333;
|
||||
margin-right: 45px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.jl-tab.active {
|
||||
color: #5aa0fa;
|
||||
}
|
||||
.jl-tabs .jl-tab:last-of-type {
|
||||
margin-right: 0px;
|
||||
}
|
||||
.jl-tabs .line {
|
||||
height: 4px;
|
||||
background: #5aa0fa;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
.jl-tabs .line.mounted {
|
||||
transition: left 0.5s;
|
||||
}
|
||||
.jl-tabs.float {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 2000;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
</style>
|
||||
220
src/components/map/selectLocation.vue
Normal file
220
src/components/map/selectLocation.vue
Normal file
@@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-autocomplete
|
||||
v-model="addressLocation"
|
||||
style="width:100%;"
|
||||
popper-class="autoAddressClass"
|
||||
:fetch-suggestions="querySearchAsync"
|
||||
:trigger-on-focus="false"
|
||||
placeholder="详细地址"
|
||||
clearable="false"
|
||||
@select="handleSelect"
|
||||
:disabled="!isCanEdit"
|
||||
v-show="type!=='view'"
|
||||
@input="addressChangeHandle"
|
||||
>
|
||||
<template slot-scope="{ item }">
|
||||
<div style="overflow:hidden;">
|
||||
<div class="title">{{ item.title }}</div>
|
||||
<span class="address ellipsis">{{ item.address }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-autocomplete>
|
||||
|
||||
<div ref="mapContainer" style="width:100%;height:300px;" v-if="isCanEdit" />
|
||||
<div ref="viewMap" style="width:100%;height:300px;" v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* eslint-disable */
|
||||
import { getcoder, querySearch } from "@/api/tenant/map";
|
||||
export default {
|
||||
props: {
|
||||
lat: {
|
||||
type: "string",
|
||||
default: 23.1315381,
|
||||
},
|
||||
lng: {
|
||||
type: "string",
|
||||
default: 113.3324436,
|
||||
},
|
||||
address: String,
|
||||
isCanEdit: Boolean,
|
||||
type: {
|
||||
type: "string",
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
map: "", // 地图实例
|
||||
mk: "", // Marker实例
|
||||
map2: "", // 地图实例
|
||||
mk2: "", // Marker实例,
|
||||
addressLocation: this.address,
|
||||
key: "FW3BZ-6JTK6-GCUS5-MZCRR-3GPR5-HJFEI",
|
||||
addressTip: [],
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.initMap();
|
||||
},
|
||||
watch: {},
|
||||
methods: {
|
||||
addressChangeHandle(val){
|
||||
if(val == ""){
|
||||
this.$emit('addressDel',"删除了地址数据")
|
||||
}
|
||||
|
||||
},
|
||||
// 初始化地图
|
||||
initMap () {
|
||||
var that = this;
|
||||
|
||||
if (this.isCanEdit) {
|
||||
var point = new qq.maps.LatLng(this.lat, this.lng);
|
||||
// 1、挂载第一张地图
|
||||
this.map = new qq.maps.Map(this.$refs.mapContainer, {
|
||||
center: point,
|
||||
zoom: 19,
|
||||
//启用缩放控件
|
||||
zoomControl: true,
|
||||
//地图缩放控件参数
|
||||
zoomControlOptions: {
|
||||
position: qq.maps.ControlPosition.TOP_LEFT,
|
||||
},
|
||||
//地图比例尺控件,若为false则不显示比例尺控件
|
||||
scaleControl: false,
|
||||
});
|
||||
|
||||
// 3、设置图像标注并绑定拖拽标注结束后事件
|
||||
this.mk = new qq.maps.Marker({
|
||||
position: point,
|
||||
draggable: true,
|
||||
map: that.map,
|
||||
});
|
||||
// 绑定拖拽标注结束后事件
|
||||
qq.maps.event.addListener(this.mk, "dragend", function (e) {
|
||||
that.getAddrByPoint(e.latLng);
|
||||
});
|
||||
//6、浏览器定位
|
||||
if (this.type === "add") {
|
||||
this.geolocation();
|
||||
}
|
||||
// 7、绑定点击地图任意点事件
|
||||
qq.maps.event.addListener(this.map, "click", function (e) {
|
||||
that.getAddrByPoint(e.latLng);
|
||||
});
|
||||
} else {
|
||||
var point2 = new qq.maps.LatLng(this.lat, this.lng);
|
||||
// 1、挂载第二张地图
|
||||
this.map2 = new qq.maps.Map(this.$refs.viewMap, {
|
||||
center: point2,
|
||||
zoom: 19,
|
||||
//地图缩放控件参数
|
||||
zoomControlOptions: {
|
||||
position: qq.maps.ControlPosition.TOP_LEFT,
|
||||
},
|
||||
//地图比例尺控件,若为false则不显示比例尺控件
|
||||
scaleControl: false,
|
||||
});
|
||||
// 2、设置图像标注并绑定拖拽标注结束后事件
|
||||
this.mk2 = new qq.maps.Marker({
|
||||
position: point2,
|
||||
draggable: false,
|
||||
map: that.map2,
|
||||
});
|
||||
}
|
||||
},
|
||||
// 浏览器定位函数
|
||||
geolocation () {
|
||||
var that = this;
|
||||
var geolocation = new qq.maps.Geolocation(
|
||||
"FW3BZ-6JTK6-GCUS5-MZCRR-3GPR5-HJFEI",
|
||||
"jobslink 企业"
|
||||
);
|
||||
geolocation.getLocation(this.showPosition, this.showErr, {
|
||||
timeout: 20000,
|
||||
failTipFlag: true,
|
||||
});
|
||||
},
|
||||
showPosition (position) {
|
||||
this.getAddrByPoint({
|
||||
lat: position.lat,
|
||||
lng: position.lng,
|
||||
});
|
||||
},
|
||||
showErr () {
|
||||
console.log("定位失败!");
|
||||
},
|
||||
// // 2、逆地址解析函数
|
||||
getAddrByPoint (point) {
|
||||
var currentPoint = new qq.maps.LatLng(point.lat, point.lng);
|
||||
var that = this;
|
||||
var location = point.lat + "," + point.lng;
|
||||
getcoder(location, encodeURI(this.key), 0).then((res) => {
|
||||
that.mk.setPosition(currentPoint);
|
||||
that.map.panTo(currentPoint);
|
||||
let obj = {
|
||||
address: res.data.result.address,
|
||||
lng: point.lng,
|
||||
lat: point.lat,
|
||||
};
|
||||
that.addressLocation = obj.address
|
||||
that.$emit("addAddress", obj);
|
||||
});
|
||||
},
|
||||
async querySearchAsync (queryString, cb) {
|
||||
await querySearch(queryString, encodeURI(this.key)).then((res) => {
|
||||
if (res.data.status === 0) {
|
||||
this.addressTip = res.data.data;
|
||||
}
|
||||
});
|
||||
var results = this.addressTip;
|
||||
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(() => {
|
||||
cb(results);
|
||||
}, 1000 * Math.random());
|
||||
},
|
||||
// 8-2、选择地址
|
||||
handleSelect (item) {
|
||||
this.addressLocation = item.address;
|
||||
var point = new qq.maps.LatLng(item.location.lat, item.location.lng);
|
||||
this.mk.setPosition(point);
|
||||
this.map.panTo(point);
|
||||
let obj = {
|
||||
title: item.title,
|
||||
address: item.address,
|
||||
lng: item.location.lng,
|
||||
lat: item.location.lat,
|
||||
};
|
||||
this.$emit("addAddress", obj);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.autoAddressClass {
|
||||
li {
|
||||
i.el-icon-search {
|
||||
margin-top: 11px;
|
||||
}
|
||||
.mgr10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.title {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
.address {
|
||||
line-height: 1;
|
||||
font-size: 12px;
|
||||
color: #b4b4b4;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
29
src/components/noSign/main.vue
Normal file
29
src/components/noSign/main.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<el-dialog append-to-body title="提示" :visible.sync="dialogVisible" width="30%" :before-close="handleClose">
|
||||
<span class="noSignText">您还未开通电子签章功能,请前往“农民工用工管理”的“电子签章管理”中申请开通电子签章功能</span>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.dialogVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.noSignText {
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
147
src/components/resume/index.vue
Normal file
147
src/components/resume/index.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<el-drawer title="个人简历" append-to-body :visible.sync="drawer" size="60%">
|
||||
<div class="page" v-loading="loading">
|
||||
<div class="title">
|
||||
<b style="font-size:16px;">基础信息</b>
|
||||
</div>
|
||||
|
||||
<div class="basic-info">
|
||||
<div class="avatar">
|
||||
<img src="/manage/svg/avatar.svg" style="display:inline-block;width:100%;height:auto;" />
|
||||
<i
|
||||
class="iconfont iconnan"
|
||||
style="position:absolute;top:5px;right:5px;"
|
||||
v-if="data.age===1"
|
||||
></i>
|
||||
<i class="iconfont iconnv" style="position:absolute;top:5px;right:5px;" v-else></i>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>{{data.realName}}</td>
|
||||
<td>{{data.age}}岁</td>
|
||||
<td style="padding-left:120px">身份</td>
|
||||
<td>灵活用工</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>联系方式</td>
|
||||
<td>{{data.telphone}}</td>
|
||||
<td style="padding-left:120px">期望地点</td>
|
||||
<td>{{getCity(data.cityId)}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="title">
|
||||
<b>期望行业</b>
|
||||
</div>
|
||||
<div class="content">
|
||||
<el-tag v-for="(item,index) in data.list" :key="index">{{item.trade}}</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="title">
|
||||
<b>具备技能</b>
|
||||
</div>
|
||||
<div class="content">
|
||||
<el-tag type="success" v-for="(item,index) in data.list" :key="index">{{item.worktypes}}</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="title">
|
||||
<b>技能水平</b>
|
||||
</div>
|
||||
<div class="content">
|
||||
<el-tag type="danger" v-for="(item,index) in data.list" :key="index">{{item.skills}}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { getDetail } from "@/api/tenant/resume";
|
||||
// import {mapGetters} from "vuex";
|
||||
|
||||
export default {
|
||||
comments: {
|
||||
|
||||
},
|
||||
components: {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
userId: '',
|
||||
loading: false,
|
||||
drawer: false,
|
||||
data: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
getCity (val) {
|
||||
if (val) {
|
||||
return this.$store.getters.getAreaParents(val)[0].label + this.$store.getters.getAreaParents(val)[1].label
|
||||
}
|
||||
},
|
||||
/*打开drawer*/
|
||||
openDialog (row) {
|
||||
this.userId = row.userId;
|
||||
this.drawer = true;
|
||||
this.getDetail();
|
||||
},
|
||||
/*加载数据*/
|
||||
getDetail () {
|
||||
this.loading = true;
|
||||
getDetail(this.userId).then(res => {
|
||||
const data = res.data.data;
|
||||
this.data = data;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.page {
|
||||
/* padding: 0 30px; */
|
||||
}
|
||||
.page .title {
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.page .basic-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.page .basic-info .avatar {
|
||||
position: relative;
|
||||
width: 120px;
|
||||
margin-right: 100px;
|
||||
}
|
||||
.page .basic-info table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0px 20px;
|
||||
}
|
||||
.page .basic-info table tr td {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.page .content {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.el-tag {
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
178
src/components/tag-select/main.vue
Normal file
178
src/components/tag-select/main.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tag
|
||||
class="tag-select-tag"
|
||||
:key="index"
|
||||
v-for="(tag,index) in value"
|
||||
:disable-transitions="true"
|
||||
closable
|
||||
@close="removeSkill(tag[prop.value], true)"
|
||||
size="medium"
|
||||
>{{ tag[prop.label] }}</el-tag
|
||||
>
|
||||
<slot name="dialog-btn" v-if="canSelect">
|
||||
<el-button class="button-new-tag" size="small" @click="showSelect(true)"
|
||||
>+ 选择{{ name }}</el-button
|
||||
>
|
||||
</slot>
|
||||
<el-dialog
|
||||
:title="name"
|
||||
:visible.sync="dialogVisible"
|
||||
width="30%"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:append-to-body="true"
|
||||
v-loading="loading"
|
||||
>
|
||||
<div>
|
||||
<el-tag
|
||||
class="tag-select-tag"
|
||||
:key="key"
|
||||
v-for="(value, key) in selected"
|
||||
closable
|
||||
type="primary"
|
||||
:disable-transitions="true"
|
||||
@close="removeSkill(key)"
|
||||
size="medium"
|
||||
>{{ value }}</el-tag
|
||||
>
|
||||
</div>
|
||||
<div class="tag-select-tip">请选择您所需的{{ name }}</div>
|
||||
<div class="tag-select-searchinp">
|
||||
<el-input
|
||||
v-model="search"
|
||||
placeholder="搜索您想要的标签"
|
||||
clearable=""
|
||||
></el-input>
|
||||
</div>
|
||||
<div>
|
||||
<el-tag
|
||||
class="tag-select-tag"
|
||||
type="info"
|
||||
:disable-transitions="true"
|
||||
size="medium"
|
||||
effect="plain"
|
||||
v-for="(tag,index) in noSelect"
|
||||
:key="index"
|
||||
@click="editSkill(tag)"
|
||||
>{{ tag[prop.label] }}</el-tag
|
||||
>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button
|
||||
icon="el-icon-circle-plus-outline"
|
||||
type="primary"
|
||||
@click="submit"
|
||||
>保 存</el-button
|
||||
>
|
||||
<el-button icon="el-icon-circle-close" @click="dialogVisible = false"
|
||||
>取 消</el-button
|
||||
>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from "lodash";
|
||||
|
||||
export default {
|
||||
name: "tagSelect",
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
name: String,
|
||||
data: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
canSelect: { type: Boolean, default: true },
|
||||
loading: Boolean,
|
||||
prop: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
label: "label",
|
||||
value: "value",
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
search: "",
|
||||
dialogVisible: false,
|
||||
selected: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
noSelect() {
|
||||
let data = this.data.filter((item) => {
|
||||
return !this.selected.hasOwnProperty(item[this.prop.value]);
|
||||
});
|
||||
if (this.search) {
|
||||
data = this.data.filter((item) => {
|
||||
return (
|
||||
item[this.prop.label]
|
||||
.toLowerCase()
|
||||
.indexOf(this.search.toLowerCase()) > -1
|
||||
);
|
||||
});
|
||||
}
|
||||
return data;
|
||||
},
|
||||
},
|
||||
watch: {},
|
||||
methods: {
|
||||
/*技能*/
|
||||
editSkill(tag) {
|
||||
this.$set(this.selected, tag[this.prop.value], tag[this.prop.label]);
|
||||
},
|
||||
removeSkill(key, save) {
|
||||
this.$delete(this.selected, key);
|
||||
if (save) {
|
||||
this.submit();
|
||||
}
|
||||
},
|
||||
submit() {
|
||||
const res = [];
|
||||
for (const key in this.selected) {
|
||||
const item = {};
|
||||
item[this.prop.label] = this.selected[key];
|
||||
item[this.prop.value] = key;
|
||||
res.push(item);
|
||||
}
|
||||
this.$emit("input", res);
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
showSelect(flag) {
|
||||
const value = _.cloneDeep(this.value || []);
|
||||
let selected = {};
|
||||
value.forEach((element) => {
|
||||
selected[element[this.prop.value]] = element[this.prop.label];
|
||||
});
|
||||
this.selected = selected;
|
||||
this.dialogVisible = flag;
|
||||
},
|
||||
},
|
||||
created(){
|
||||
this.showSelect();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.tag-select-tag {
|
||||
margin: 0 5px 5px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tag-select-tip,
|
||||
.tag-select-searchinp {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
</style>
|
||||
25
src/components/tooltip/index.vue
Normal file
25
src/components/tooltip/index.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<el-tooltip effect="dark" placement="right">
|
||||
<div slot="content">
|
||||
1.选择相应的搜索时间类型
|
||||
<br/>
|
||||
2.选择相应的搜索时间范围
|
||||
<br>
|
||||
3.点击“搜索”按钮查看详情
|
||||
</div>
|
||||
<img src="./tip.png" height="26" width="26" class="toolTipImg"/>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "index"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.toolTipImg{
|
||||
margin-bottom: -5px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
BIN
src/components/tooltip/tip.png
Normal file
BIN
src/components/tooltip/tip.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
468
src/components/verifition/Verify.vue
Normal file
468
src/components/verifition/Verify.vue
Normal file
File diff suppressed because one or more lines are too long
252
src/components/verifition/Verify/VerifyPoints.vue
Normal file
252
src/components/verifition/Verify/VerifyPoints.vue
Normal file
@@ -0,0 +1,252 @@
|
||||
<template>
|
||||
<div style="position: relative">
|
||||
<div class="verify-img-out">
|
||||
<div
|
||||
class="verify-img-panel"
|
||||
:style="{'width': setSize.imgWidth,
|
||||
'height': setSize.imgHeight,
|
||||
'background-size' : setSize.imgWidth + ' '+ setSize.imgHeight,
|
||||
'margin-bottom': vSpace + 'px'}"
|
||||
>
|
||||
<div class="verify-refresh" style="z-index:3" @click="refresh" v-show="showRefresh">
|
||||
<i class="iconfont icon-refresh"></i>
|
||||
</div>
|
||||
<img
|
||||
:src="'data:image/png;base64,'+pointBackImgBase"
|
||||
ref="canvas"
|
||||
alt
|
||||
style="width:100%;height:100%;display:block"
|
||||
@click="bindingClick?canvasClick($event):undefined"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-for="(tempPoint, index) in tempPoints"
|
||||
:key="index"
|
||||
class="point-area"
|
||||
:style="{
|
||||
'background-color':'#1abd6c',
|
||||
color:'#fff',
|
||||
'z-index':9999,
|
||||
width:'20px',
|
||||
height:'20px',
|
||||
'text-align':'center',
|
||||
'line-height':'20px',
|
||||
'border-radius': '50%',
|
||||
position:'absolute',
|
||||
top:parseInt(tempPoint.y-10) + 'px',
|
||||
left:parseInt(tempPoint.x-10) + 'px'
|
||||
}"
|
||||
>{{index + 1}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 'height': this.barSize.height, -->
|
||||
<div
|
||||
class="verify-bar-area"
|
||||
:style="{'width': setSize.imgWidth,
|
||||
'color': this.barAreaColor,
|
||||
'border-color': this.barAreaBorderColor,
|
||||
'line-height':this.barSize.height}"
|
||||
>
|
||||
<span class="verify-msg">{{text}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script type="text/babel">
|
||||
/**
|
||||
* VerifyPoints
|
||||
* @description 点选
|
||||
* */
|
||||
import { resetSize } from './../utils/util'
|
||||
import { aesEncrypt } from "./../utils/ase"
|
||||
import { reqGet, reqCheck } from "./../api/index"
|
||||
|
||||
export default {
|
||||
name: 'VerifyPoints',
|
||||
props: {
|
||||
//弹出式pop,固定fixed
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'fixed'
|
||||
},
|
||||
captchaType: {
|
||||
type: String,
|
||||
},
|
||||
//间隔
|
||||
vSpace: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
imgSize: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
width: '310px',
|
||||
height: '155px'
|
||||
}
|
||||
}
|
||||
},
|
||||
barSize: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
width: '310px',
|
||||
height: '40px'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
secretKey: '', //后端返回的ase加密秘钥
|
||||
checkNum: 3, //默认需要点击的字数
|
||||
fontPos: [], //选中的坐标信息
|
||||
checkPosArr: [], //用户点击的坐标
|
||||
num: 1, //点击的记数
|
||||
pointBackImgBase: '', //后端获取到的背景图片
|
||||
poinTextList: [], //后端返回的点击字体顺序
|
||||
backToken: '', //后端返回的token值
|
||||
setSize: {
|
||||
imgHeight: 0,
|
||||
imgWidth: 0,
|
||||
barHeight: 0,
|
||||
barWidth: 0
|
||||
},
|
||||
tempPoints: [],
|
||||
text: '',
|
||||
barAreaColor: undefined,
|
||||
barAreaBorderColor: undefined,
|
||||
showRefresh: true,
|
||||
bindingClick: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
resetSize () {
|
||||
return resetSize
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
//加载页面
|
||||
this.fontPos.splice(0, this.fontPos.length)
|
||||
this.checkPosArr.splice(0, this.checkPosArr.length)
|
||||
this.num = 1
|
||||
this.getPictrue();
|
||||
this.$nextTick(() => {
|
||||
this.setSize = this.resetSize(this) //重新设置宽度高度
|
||||
this.$parent.$emit('ready', this)
|
||||
})
|
||||
},
|
||||
canvasClick (e) {
|
||||
this.checkPosArr.push(this.getMousePos(this.$refs.canvas, e));
|
||||
if (this.num == this.checkNum) {
|
||||
this.num = this.createPoint(this.getMousePos(this.$refs.canvas, e));
|
||||
//按比例转换坐标值
|
||||
this.checkPosArr = this.pointTransfrom(this.checkPosArr, this.setSize);
|
||||
//等创建坐标执行完
|
||||
setTimeout(() => {
|
||||
// var flag = this.comparePos(this.fontPos, this.checkPosArr);
|
||||
//发送后端请求
|
||||
var captchaVerification = this.secretKey ? aesEncrypt(this.backToken + '---' + JSON.stringify(this.checkPosArr), this.secretKey) : this.backToken + '---' + JSON.stringify(this.checkPosArr)
|
||||
let data = {
|
||||
captchaType: this.captchaType,
|
||||
"pointJson": this.secretKey ? aesEncrypt(JSON.stringify(this.checkPosArr), this.secretKey) : JSON.stringify(this.checkPosArr),
|
||||
"token": this.backToken
|
||||
}
|
||||
reqCheck(data).then(res => {
|
||||
if (res.repCode == "0000") {
|
||||
this.barAreaColor = '#4cae4c'
|
||||
this.barAreaBorderColor = '#5cb85c'
|
||||
this.text = '验证成功'
|
||||
this.bindingClick = false
|
||||
if (this.mode == 'pop') {
|
||||
setTimeout(() => {
|
||||
this.$parent.clickShow = false;
|
||||
this.refresh();
|
||||
}, 1500)
|
||||
}
|
||||
this.$parent.$emit('success', { captchaVerification })
|
||||
} else {
|
||||
this.$parent.$emit('error', this)
|
||||
this.barAreaColor = '#d9534f'
|
||||
this.barAreaBorderColor = '#d9534f'
|
||||
this.text = '验证失败'
|
||||
setTimeout(() => {
|
||||
this.refresh();
|
||||
}, 700);
|
||||
}
|
||||
})
|
||||
}, 400);
|
||||
}
|
||||
if (this.num < this.checkNum) {
|
||||
this.num = this.createPoint(this.getMousePos(this.$refs.canvas, e));
|
||||
}
|
||||
},
|
||||
|
||||
//获取坐标
|
||||
getMousePos: function (obj, e) {
|
||||
var x = e.offsetX
|
||||
var y = e.offsetY
|
||||
return { x, y }
|
||||
},
|
||||
//创建坐标点
|
||||
createPoint: function (pos) {
|
||||
this.tempPoints.push(Object.assign({}, pos))
|
||||
return ++this.num;
|
||||
},
|
||||
refresh: function () {
|
||||
this.tempPoints.splice(0, this.tempPoints.length)
|
||||
this.barAreaColor = '#000'
|
||||
this.barAreaBorderColor = '#ddd'
|
||||
this.bindingClick = true
|
||||
this.fontPos.splice(0, this.fontPos.length)
|
||||
this.checkPosArr.splice(0, this.checkPosArr.length)
|
||||
this.num = 1
|
||||
this.getPictrue();
|
||||
this.text = '验证失败'
|
||||
this.showRefresh = true
|
||||
},
|
||||
|
||||
// 请求背景图片和验证图片
|
||||
getPictrue () {
|
||||
let data = {
|
||||
captchaType: this.captchaType
|
||||
}
|
||||
reqGet(data).then(res => {
|
||||
if (res.repCode == "0000") {
|
||||
this.pointBackImgBase = res.repData.originalImageBase64
|
||||
this.backToken = res.repData.token
|
||||
this.secretKey = res.repData.secretKey
|
||||
this.poinTextList = res.repData.wordList
|
||||
this.text = '请依次点击【' + this.poinTextList.join(",") + '】'
|
||||
} else {
|
||||
this.text = res.repMsg;
|
||||
}
|
||||
})
|
||||
},
|
||||
//坐标转换函数
|
||||
pointTransfrom (pointArr, imgSize) {
|
||||
var newPointArr = pointArr.map(p => {
|
||||
let x = Math.round(310 * p.x / parseInt(imgSize.imgWidth))
|
||||
let y = Math.round(155 * p.y / parseInt(imgSize.imgHeight))
|
||||
return { x, y }
|
||||
})
|
||||
return newPointArr
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// type变化则全面刷新
|
||||
type: {
|
||||
immediate: true,
|
||||
handler () {
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// 禁止拖拽
|
||||
this.$el.onselectstart = function () {
|
||||
return false
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
375
src/components/verifition/Verify/VerifySlide.vue
Normal file
375
src/components/verifition/Verify/VerifySlide.vue
Normal file
@@ -0,0 +1,375 @@
|
||||
<template>
|
||||
<div style="position: relative;">
|
||||
<div
|
||||
v-if="type === '2'"
|
||||
class="verify-img-out"
|
||||
:style="{height: (parseInt(setSize.imgHeight) + vSpace) + 'px'}"
|
||||
>
|
||||
<div
|
||||
class="verify-img-panel"
|
||||
:style="{width: setSize.imgWidth,
|
||||
height: setSize.imgHeight,}"
|
||||
>
|
||||
<img
|
||||
:src="'data:image/png;base64,'+backImgBase"
|
||||
alt
|
||||
style="width:100%;height:100%;display:block"
|
||||
/>
|
||||
<div class="verify-refresh" @click="refresh" v-show="showRefresh">
|
||||
<i class="iconfont icon-refresh"></i>
|
||||
</div>
|
||||
<transition name="tips">
|
||||
<span
|
||||
class="verify-tips"
|
||||
v-if="tipWords"
|
||||
:class="passFlag ?'suc-bg':'err-bg'"
|
||||
>{{tipWords}}</span>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 公共部分 -->
|
||||
<div
|
||||
class="verify-bar-area"
|
||||
:style="{width: setSize.imgWidth,
|
||||
height: barSize.height,
|
||||
'line-height':barSize.height}"
|
||||
>
|
||||
<span class="verify-msg" v-text="text"></span>
|
||||
<div
|
||||
class="verify-left-bar"
|
||||
:style="{width: (leftBarWidth!==undefined)?leftBarWidth: barSize.height, height: barSize.height, 'border-color': leftBarBorderColor, transaction: transitionWidth}"
|
||||
>
|
||||
<span class="verify-msg" v-text="finishText"></span>
|
||||
<div
|
||||
class="verify-move-block"
|
||||
@touchstart="start"
|
||||
@mousedown="start"
|
||||
:style="{width: barSize.height, height: barSize.height, 'background-color': moveBlockBackgroundColor, left: moveBlockLeft, transition: transitionLeft}"
|
||||
>
|
||||
<i :class="['verify-icon iconfont', iconClass]" :style="{color: iconColor}"></i>
|
||||
<div
|
||||
v-if="type === '2'"
|
||||
class="verify-sub-block"
|
||||
:style="{'width':Math.floor(parseInt(setSize.imgWidth)*47/310)+ 'px',
|
||||
'height': setSize.imgHeight,
|
||||
'top':'-' + (parseInt(setSize.imgHeight) + vSpace) + 'px',
|
||||
'background-size': setSize.imgWidth + ' ' + setSize.imgHeight,
|
||||
}"
|
||||
>
|
||||
<img
|
||||
:src="'data:image/png;base64,'+blockBackImgBase"
|
||||
alt
|
||||
style="width:100%;height:100%;display:block"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script type="text/babel">
|
||||
/**
|
||||
* VerifySlide
|
||||
* @description 滑块
|
||||
* */
|
||||
import { aesEncrypt } from "../utils/ase"
|
||||
import { resetSize } from '../utils/util'
|
||||
import { reqGet, reqCheck } from "../api/index"
|
||||
|
||||
// "captchaType":"blockPuzzle",
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
name: 'VerifySlide',
|
||||
props: {
|
||||
captchaType: {
|
||||
type: String,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '1'
|
||||
},
|
||||
//弹出式pop,固定fixed
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'fixed'
|
||||
},
|
||||
vSpace: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
explain: {
|
||||
type: String,
|
||||
default: '向右滑动完成验证'
|
||||
},
|
||||
imgSize: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
width: '310px',
|
||||
height: '155px'
|
||||
}
|
||||
}
|
||||
},
|
||||
blockSize: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
width: '50px',
|
||||
height: '50px'
|
||||
}
|
||||
}
|
||||
},
|
||||
barSize: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
width: '310px',
|
||||
height: '40px'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
secretKey: '', //后端返回的加密秘钥 字段
|
||||
passFlag: '', //是否通过的标识
|
||||
backImgBase: '', //验证码背景图片
|
||||
blockBackImgBase: '', //验证滑块的背景图片
|
||||
backToken: "", //后端返回的唯一token值
|
||||
startMoveTime: "", //移动开始的时间
|
||||
endMovetime: '', //移动结束的时间
|
||||
tipsBackColor: '', //提示词的背景颜色
|
||||
tipWords: '',
|
||||
text: '',
|
||||
finishText: '',
|
||||
setSize: {
|
||||
imgHeight: 0,
|
||||
imgWidth: 0,
|
||||
barHeight: 0,
|
||||
barWidth: 0
|
||||
},
|
||||
top: 0,
|
||||
left: 0,
|
||||
moveBlockLeft: undefined,
|
||||
leftBarWidth: undefined,
|
||||
// 移动中样式
|
||||
moveBlockBackgroundColor: undefined,
|
||||
leftBarBorderColor: '#ddd',
|
||||
iconColor: undefined,
|
||||
iconClass: 'icon-right',
|
||||
status: false, //鼠标状态
|
||||
isEnd: false, //是够验证完成
|
||||
showRefresh: true,
|
||||
transitionLeft: '',
|
||||
transitionWidth: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
barArea () {
|
||||
return this.$el.querySelector('.verify-bar-area')
|
||||
},
|
||||
resetSize () {
|
||||
return resetSize
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
this.text = this.explain
|
||||
this.getPictrue();
|
||||
this.$nextTick(() => {
|
||||
let setSize = this.resetSize(this) //重新设置宽度高度
|
||||
for (let key in setSize) {
|
||||
this.$set(this.setSize, key, setSize[key])
|
||||
}
|
||||
this.$parent.$emit('ready', this)
|
||||
})
|
||||
|
||||
var _this = this
|
||||
|
||||
window.removeEventListener("touchmove", function (e) {
|
||||
_this.move(e);
|
||||
});
|
||||
window.removeEventListener("mousemove", function (e) {
|
||||
_this.move(e);
|
||||
});
|
||||
|
||||
//鼠标松开
|
||||
window.removeEventListener("touchend", function () {
|
||||
_this.end();
|
||||
});
|
||||
window.removeEventListener("mouseup", function () {
|
||||
_this.end();
|
||||
});
|
||||
|
||||
window.addEventListener("touchmove", function (e) {
|
||||
_this.move(e);
|
||||
});
|
||||
window.addEventListener("mousemove", function (e) {
|
||||
_this.move(e);
|
||||
});
|
||||
|
||||
//鼠标松开
|
||||
window.addEventListener("touchend", function () {
|
||||
_this.end();
|
||||
});
|
||||
window.addEventListener("mouseup", function () {
|
||||
_this.end();
|
||||
});
|
||||
},
|
||||
|
||||
//鼠标按下
|
||||
start: function (e) {
|
||||
e = e || window.event
|
||||
if (!e.touches) { //兼容PC端
|
||||
var x = e.clientX;
|
||||
} else { //兼容移动端
|
||||
var x = e.touches[0].pageX;
|
||||
}
|
||||
this.startLeft = Math.floor(x - this.barArea.getBoundingClientRect().left);
|
||||
this.startMoveTime = +new Date(); //开始滑动的时间
|
||||
if (this.isEnd == false) {
|
||||
this.text = ''
|
||||
this.moveBlockBackgroundColor = '#337ab7'
|
||||
this.leftBarBorderColor = '#337AB7'
|
||||
this.iconColor = '#fff'
|
||||
e.stopPropagation();
|
||||
this.status = true;
|
||||
}
|
||||
},
|
||||
//鼠标移动
|
||||
move: function (e) {
|
||||
e = e || window.event
|
||||
if (this.status && this.isEnd == false) {
|
||||
if (!e.touches) { //兼容PC端
|
||||
var x = e.clientX;
|
||||
} else { //兼容移动端
|
||||
var x = e.touches[0].pageX;
|
||||
}
|
||||
var bar_area_left = this.barArea.getBoundingClientRect().left;
|
||||
var move_block_left = x - bar_area_left //小方块相对于父元素的left值
|
||||
if (move_block_left >= this.barArea.offsetWidth - parseInt(parseInt(this.blockSize.width) / 2) - 2) {
|
||||
move_block_left = this.barArea.offsetWidth - parseInt(parseInt(this.blockSize.width) / 2) - 2;
|
||||
}
|
||||
if (move_block_left <= 0) {
|
||||
move_block_left = parseInt(parseInt(this.blockSize.width) / 2);
|
||||
}
|
||||
//拖动后小方块的left值
|
||||
this.moveBlockLeft = (move_block_left - this.startLeft) + "px"
|
||||
this.leftBarWidth = (move_block_left - this.startLeft) + "px"
|
||||
}
|
||||
},
|
||||
|
||||
//鼠标松开
|
||||
end: function () {
|
||||
this.endMovetime = +new Date();
|
||||
var _this = this;
|
||||
//判断是否重合
|
||||
if (this.status && this.isEnd == false) {
|
||||
var moveLeftDistance = parseInt((this.moveBlockLeft || '').replace('px', ''));
|
||||
moveLeftDistance = moveLeftDistance * 310 / parseInt(this.setSize.imgWidth)
|
||||
let data = {
|
||||
captchaType: this.captchaType,
|
||||
"pointJson": this.secretKey ? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), this.secretKey) : JSON.stringify({ x: moveLeftDistance, y: 5.0 }),
|
||||
"token": this.backToken
|
||||
}
|
||||
reqCheck(data).then(res => {
|
||||
if (res.repCode == "0000") {
|
||||
this.moveBlockBackgroundColor = '#5cb85c'
|
||||
this.leftBarBorderColor = '#5cb85c'
|
||||
this.iconColor = '#fff'
|
||||
this.iconClass = 'icon-check'
|
||||
this.showRefresh = false
|
||||
this.isEnd = true;
|
||||
if (this.mode == 'pop') {
|
||||
setTimeout(() => {
|
||||
this.$parent.clickShow = false;
|
||||
this.refresh();
|
||||
}, 1500)
|
||||
}
|
||||
this.passFlag = true
|
||||
this.tipWords = `${((this.endMovetime - this.startMoveTime) / 1000).toFixed(2)}s验证成功`
|
||||
var captchaVerification = this.secretKey ? aesEncrypt(this.backToken + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 }), this.secretKey) : this.backToken + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 })
|
||||
setTimeout(() => {
|
||||
this.tipWords = ""
|
||||
this.$parent.closeBox();
|
||||
this.$parent.$emit('success', { captchaVerification })
|
||||
}, 1000)
|
||||
} else {
|
||||
this.moveBlockBackgroundColor = '#d9534f'
|
||||
this.leftBarBorderColor = '#d9534f'
|
||||
this.iconColor = '#fff'
|
||||
this.iconClass = 'icon-close'
|
||||
this.passFlag = false
|
||||
setTimeout(function () {
|
||||
_this.refresh();
|
||||
}, 1000);
|
||||
this.$parent.$emit('error', this)
|
||||
this.tipWords = "验证失败"
|
||||
setTimeout(() => {
|
||||
this.tipWords = ""
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
this.status = false;
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function () {
|
||||
this.showRefresh = true
|
||||
this.finishText = ''
|
||||
|
||||
this.transitionLeft = 'left .3s'
|
||||
this.moveBlockLeft = 0
|
||||
|
||||
this.leftBarWidth = undefined
|
||||
this.transitionWidth = 'width .3s'
|
||||
|
||||
this.leftBarBorderColor = '#ddd'
|
||||
this.moveBlockBackgroundColor = '#fff'
|
||||
this.iconColor = '#000'
|
||||
this.iconClass = 'icon-right'
|
||||
this.isEnd = false
|
||||
|
||||
this.getPictrue()
|
||||
setTimeout(() => {
|
||||
this.transitionWidth = ''
|
||||
this.transitionLeft = ''
|
||||
this.text = this.explain
|
||||
}, 300)
|
||||
},
|
||||
|
||||
// 请求背景图片和验证图片
|
||||
getPictrue () {
|
||||
let data = {
|
||||
captchaType: this.captchaType
|
||||
}
|
||||
reqGet(data).then(res => {
|
||||
if (res.repCode == "0000") {
|
||||
this.backImgBase = res.repData.originalImageBase64
|
||||
this.blockBackImgBase = res.repData.jigsawImageBase64
|
||||
this.backToken = res.repData.token
|
||||
this.secretKey = res.repData.secretKey
|
||||
} else {
|
||||
this.tipWords = res.repMsg;
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// type变化则全面刷新
|
||||
type: {
|
||||
immediate: true,
|
||||
handler () {
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// 禁止拖拽
|
||||
this.$el.onselectstart = function () {
|
||||
return false
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
27
src/components/verifition/api/index.js
Normal file
27
src/components/verifition/api/index.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 此处可直接引用自己项目封装好的 axios 配合后端联调
|
||||
*/
|
||||
|
||||
|
||||
import request from '@/router/axios';
|
||||
// import request from "@/api/axios.js" //调用项目封装的axios
|
||||
|
||||
//获取验证图片 以及token
|
||||
export function reqGet (data) {
|
||||
return request({
|
||||
url: '/captcha/get',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
//滑动或者点选验证
|
||||
export function reqCheck (data) {
|
||||
return request({
|
||||
url: '/captcha/check',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
11
src/components/verifition/utils/ase.js
Normal file
11
src/components/verifition/utils/ase.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import CryptoJS from 'crypto-js'
|
||||
/**
|
||||
* @word 要加密的内容
|
||||
* @keyWord String 服务器随机返回的关键字
|
||||
* */
|
||||
export function aesEncrypt (word, keyWord = "XwKsGlMcdPMEhR1B") {
|
||||
var key = CryptoJS.enc.Utf8.parse(keyWord);
|
||||
var srcs = CryptoJS.enc.Utf8.parse(word);
|
||||
var encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 });
|
||||
return encrypted.toString();
|
||||
}
|
||||
36
src/components/verifition/utils/util.js
Normal file
36
src/components/verifition/utils/util.js
Normal file
@@ -0,0 +1,36 @@
|
||||
export function resetSize(vm) {
|
||||
var img_width, img_height, bar_width, bar_height; //图片的宽度、高度,移动条的宽度、高度
|
||||
|
||||
var parentWidth = vm.$el.parentNode.offsetWidth || window.offsetWidth
|
||||
var parentHeight = vm.$el.parentNode.offsetHeight || window.offsetHeight
|
||||
|
||||
if (vm.imgSize.width.indexOf('%') != -1) {
|
||||
img_width = parseInt(this.imgSize.width) / 100 * parentWidth + 'px'
|
||||
} else {
|
||||
img_width = this.imgSize.width;
|
||||
}
|
||||
|
||||
if (vm.imgSize.height.indexOf('%') != -1) {
|
||||
img_height = parseInt(this.imgSize.height) / 100 * parentHeight + 'px'
|
||||
} else {
|
||||
img_height = this.imgSize.height
|
||||
}
|
||||
|
||||
if (vm.barSize.width.indexOf('%') != -1) {
|
||||
bar_width = parseInt(this.barSize.width) / 100 * parentWidth + 'px'
|
||||
} else {
|
||||
bar_width = this.barSize.width
|
||||
}
|
||||
|
||||
if (vm.barSize.height.indexOf('%') != -1) {
|
||||
bar_height = parseInt(this.barSize.height) / 100 * parentHeight + 'px'
|
||||
} else {
|
||||
bar_height = this.barSize.height
|
||||
}
|
||||
|
||||
return {imgWidth: img_width, imgHeight: img_height, barWidth: bar_width, barHeight: bar_height}
|
||||
}
|
||||
|
||||
export const _code_chars = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
|
||||
export const _code_color1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0']
|
||||
export const _code_color2 = ['#FF0033', '#006699', '#993366', '#FF9900', '#66CC66', '#FF33CC']
|
||||
Reference in New Issue
Block a user