优化管理端代码结构

This commit is contained in:
paulGao
2022-09-02 17:51:33 +08:00
parent 36c4584970
commit 55aa57d812
147 changed files with 1759 additions and 114 deletions

View File

@@ -0,0 +1,197 @@
<template>
<div>
<div class="breadcrumb">
<span @click="clickBreadcrumb(item,index)" :class="{'active':item.selected}" v-for="(item,index) in dateList"
:key="index"> {{item.title}}</span>
<div class="date-picker">
<Select @on-change="changeSelect(selectedWay)" v-model="month" placeholder="年月查询" clearable
style="width:200px;margin-left:10px;">
<Option v-for="(item,index) in dates" :value="item.year+'-'+item.month" :key="index" clearable>
{{ item.year+'年'+item.month+'月' }}</Option>
</Select>
</div>
<div class="shop-list" v-if="!closeShop">
<Select clearable @on-change="changeshop(selectedWay)" v-model="storeId" placeholder="店铺查询"
style="width:200px;margin-left:10px;">
<Scroll :on-reach-bottom="handleReachBottom">
<Option v-for="(item,index) in shopsData" :value="item.id" :key="index">{{ item.storeName }}</Option>
</Scroll>
</Select>
</div>
</div>
</div>
</template>
<script>
import { getShopListData } from "@/api/shops.js";
export default {
props: ["closeShop"],
data() {
return {
month: "", // 月份
selectedWay: {
// 可选时间项
title: "最近7天",
selected: true,
searchType: "LAST_SEVEN",
},
storeId: "", // 店铺id
dates: [], // 日期列表
params: {
// 请求参数
pageNumber: 1,
pageSize: 10,
storeName: "",
},
dateList: [
// 筛选条件
{
title: "今天",
selected: false,
searchType: "TODAY",
},
{
title: "昨天",
selected: false,
searchType: "YESTERDAY",
},
{
title: "最近7天",
selected: true,
searchType: "LAST_SEVEN",
},
{
title: "最近30天",
selected: false,
searchType: "LAST_THIRTY",
},
],
shopTotal: "", // 店铺总数
shopsData: [], // 店铺数据
};
},
mounted() {
this.getFiveYears();
this.getShopList();
},
methods: {
// 页面触底
handleReachBottom() {
setTimeout(() => {
if (this.params.pageNumber * this.params.pageSize <= this.shopTotal) {
this.params.pageNumber++;
this.getShopList();
}
}, 1500);
},
// 查询店铺列表
getShopList() {
getShopListData(this.params).then((res) => {
if (res.success) {
/**
* 解决数据请求中,滚动栏会一直上下跳动
*/
this.shopTotal = res.result.total;
this.shopsData.push(...res.result.records);
}
});
},
// 变更店铺
changeshop(val) {
this.selectedWay.storeId = this.storeId;
this.$emit("selected", this.selectedWay);
},
// 获取近5年 年月
getFiveYears() {
let getYear = new Date().getFullYear();
let lastFiveYear = getYear - 5;
let maxMonth = new Date().getMonth() + 1;
let dates = [];
// 循环出过去5年
for (let year = lastFiveYear; year <= getYear; year++) {
for (let month = 1; month <= 12; month++) {
if (year == getYear && month > maxMonth) {
} else {
dates.push({
year: year,
month: month,
});
}
}
}
this.dates = dates.reverse();
},
// 改变已选店铺
changeSelect() {
console.log(this.month);
if (this.month) {
this.dateList.forEach((res) => {
res.selected = false;
});
this.selectedWay.year = this.month.split("-")[0];
this.selectedWay.month = this.month.split("-")[1];
this.selectedWay.searchType = "";
this.$emit("selected", this.selectedWay);
} else {
}
},
// 变更时间
clickBreadcrumb(item) {
this.dateList.forEach((res) => {
res.selected = false;
});
item.selected = true;
item.storeId = this.storeId;
this.month = "";
if (item.searchType == "") {
if (
dateList.some((date) => {
return date.title == item.title;
})
) {
item.searchType = date.searchType;
} else {
item.searchType = "LAST_SEVEN";
}
}
this.selectedWay = item;
this.selectedWay.year = new Date().getFullYear();
this.selectedWay.month = "";
this.$emit("selected", this.selectedWay);
},
},
};
</script>
<style lang="scss" scoped>
.breadcrumb {
display: flex;
align-items: center;
> span {
margin-right: 15px;
cursor: pointer;
}
}
.active {
color: $theme_color;
position: relative;
}
.date-picker {
}
.active:before {
content: "";
position: absolute;
bottom: -10px;
left: 0;
width: 100%;
height: 3px;
background: $theme_color;
}
</style>

View File

@@ -0,0 +1,61 @@
import plugins from "./plugins";
import toobar from "./toolbar";
import { upLoadFile } from "@/api/common";
export const initEditor = {
height: "400px",
language: "zh_CN",
menubar: "file edit insert view format table", // 菜单:指定应该出现哪些菜单
toolbar: toobar, // 分组工具栏控件
plugins: plugins, // 插件(比如: advlist | link | image | preview等)
object_resizing: false, // 是否禁用表格图片大小调整
end_container_on_empty_block: true, // enter键 分块
powerpaste_word_import: "merge", // 是否保留word粘贴样式 clean | merge
code_dialog_height: 450, // 代码框高度 、宽度
code_dialog_width: 1000,
advlist_bullet_styles: "square", // 无序列表 有序列表
maxSize: "2097152", // 设置图片大小
accept: "image/jpeg, image/png", // 设置图片上传规则
images_upload_handler: async function (blobInfo, success, failure) {
console.log("请求")
const formData = new FormData();
formData.append("file", blobInfo.blob());
try {
const res = await upLoadFile(formData);
if (res.result) {
success(res.result)
} else {
failure("上传文件有误请稍后重试");
}
} catch (e) {
failure('上传出错')
}
},
// init_instance_callback: function (editor) {
// var freeTiny = document.querySelector(".tox .tox-notification--in");
// freeTiny.style.display = "none";
// },
content_style: `
* { padding:0; margin:0; }
html, body height:100%; }
img { max-width:100%; display:block;height:auto; }
a { text-decoration: none; }
iframe{ width: 100%; }
p { line-height:1.6; margin: 0px; }
table{ word-wrap:break-word; word-break:break-all; max-width:100%; border:none; border-color:#999; }
.mce-object-iframe{ width:100%; box-sizing:border-box; margin:0; padding:0; }
ul,ol{ list-style-position:inside; }
`, // 设置样式
statusbar: false, // 隐藏编辑器底部的状态栏
elementpath: false, // 禁用编辑器底部的状态栏
paste_data_images: true, // 允许粘贴图像
};

View File

@@ -0,0 +1,4 @@
const plugins = [
'advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'
]
export default plugins

View File

@@ -0,0 +1,2 @@
const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor ']
export default toolbar

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,657 @@
.hz-m-wrap {
position: relative;
/*overflow: hidden;*/
}
.hz-m-wrap .hz-u-img {
display: block;
width: 100%;
max-width: 100%;
height: auto;
max-height: 100%;
user-select: none;
}
.hz-m-wrap .hz-m-area {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
cursor: crosshair;
}
.hz-m-wrap .hz-m-item {
position: absolute;
display: block;
}
.hz-m-wrap .hz-m-box {
position: relative;
width: 100%;
height: 100%;
box-shadow: 0 0 6px #000;
background-color: #e31414;
font-size: 12px;
cursor: pointer;
color: #fff;
opacity: 0.8;
}
.hz-m-box{
overflow: hidden;
}
.hz-m-wrap .hz-m-box>li {
position: absolute;
text-align: center;
user-select: none;
}
.hz-m-wrap .hz-m-box.hz-z-hidden>li {
display: none;
}
.hz-m-wrap .hz-m-box.hz-m-hoverbox:hover {
box-shadow: 0 0 0 2px #373950;
}
.hz-m-wrap .hz-m-box.hz-m-hoverbox .hz-icon:hover {
background-color: #373950;
}
.hz-m-wrap .hz-m-box .hz-icon {
width: 24px;
height: 24px;
line-height: 24px;
font-size: 20px;
text-align: center;
}
.hz-m-wrap .hz-m-box .hz-icon:hover {
background-color: #e31414;
opacity: 0.8;
}
.hz-m-wrap .hz-m-box .hz-u-index {
top: 0;
left: 0;
width: 24px;
height: 24px;
line-height: 24px;
background-color: #000;
z-index: 100;
}
.hz-m-wrap .hz-m-box .hz-u-close {
top: 0;
right: 0;
z-index: 10;
}
.hz-m-wrap .hz-m-box .hz-m-copy {
display: inline-block;
}
.hz-m-wrap .hz-m-box .hz-small-icon {
border: 0;
border-radius: 0;
}
.hz-m-wrap .hz-m-box .hz-u-square {
width: 8px;
height: 8px;
opacity: 0.8;
}
.hz-m-wrap .hz-m-box .hz-u-square:after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 4px;
height: 4px;
border-radius: 4px;
background-color: #fff;
}
.hz-m-wrap .hz-m-box .hz-u-square-tl {
top: -4px;
left: -4px;
cursor: nw-resize;
}
.hz-m-wrap .hz-m-box .hz-u-square-tc {
top: -4px;
left: 50%;
transform: translateX(-50%);
cursor: n-resize;
}
.hz-m-wrap .hz-m-box .hz-u-square-tr {
top: -4px;
right: -4px;
cursor: ne-resize;
}
.hz-m-wrap .hz-m-box .hz-u-square-cl {
top: 50%;
left: -4px;
transform: translateY(-50%);
cursor: w-resize;
}
.hz-m-wrap .hz-m-box .hz-u-square-cr {
top: 50%;
right: -4px;
transform: translateY(-50%);
cursor: w-resize;
}
.hz-m-wrap .hz-m-box .hz-u-square-bl {
bottom: -4px;
left: -4px;
cursor: sw-resize;
}
.hz-m-wrap .hz-m-box .hz-u-square-bc {
bottom: -4px;
left: 50%;
transform: translateX(-50%);
cursor: s-resize;
}
.hz-m-wrap .hz-m-box .hz-u-square-br {
bottom: -4px;
right: -4px;
cursor: se-resize;
}
/* reset */
.hz-m-modal,
.hz-m-wrap {
font-size: 12px;
/* 清除内外边距 */
/* 重置列表元素 */
/* 重置文本格式元素 */
/* 初始化 input */
}
.hz-m-modal ul,
.hz-m-wrap ul,
.hz-m-modal ol,
.hz-m-wrap ol,
.hz-m-modal li,
.hz-m-wrap li {
margin: 0;
padding: 0;
}
.hz-m-modal ul,
.hz-m-wrap ul,
.hz-m-modal ol,
.hz-m-wrap ol {
list-style: none;
}
.hz-m-modal a,
.hz-m-wrap a {
text-decoration: none;
}
.hz-m-modal a:hover,
.hz-m-wrap a:hover {
text-decoration: underline;
}
.hz-m-modal p,
.hz-m-wrap p {
-webkit-margin-before: 0;
-webkit-margin-after: 0;
}
.hz-m-modal input[type="checkbox"],
.hz-m-wrap input[type="checkbox"] {
cursor: pointer;
}
/* basic */
/* modal 样式 */
.hz-m-modal {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
touch-action: cross-slide-y pinch-zoom double-tap-zoom;
text-align: center;
overflow: hidden;
}
.hz-m-modal:before {
content: "";
display: inline-block;
vertical-align: middle;
height: 100%;
}
.hz-m-modal .hz-modal_dialog {
display: inline-block;
vertical-align: middle;
text-align: left;
border-radius: 3px;
}
.hz-m-modal .hz-modal_title {
margin: 0;
}
.hz-m-modal .hz-modal_close {
float: right;
margin: -6px -4px 0 0;
}
@media (max-width: 767px) {
.hz-m-modal .hz-modal_dialog {
width: auto;
}
}
html.z-modal,
html.z-modal body {
overflow: hidden;
}
.hz-m-modal {
background: rgba(0, 0, 0, 0.6);
}
.hz-m-modal .hz-modal_dialog {
width: 450px;
background: #fff;
-webkit-box-shadow: 0 2px 3px rgba(0, 0, 0, 0.125);
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.125);
}
.hz-m-modal .hz-modal_hd {
padding: 15px;
border-bottom: 1px solid #f4f4f4;
}
.hz-m-modal .hz-modal_title {
font-size: 18px;
}
.hz-m-modal .hz-modal_close {
margin: -15px -15px 0 0;
padding: 6px;
color: #bbb;
cursor: pointer;
}
.hz-m-modal .hz-modal_close:hover {
color: #888;
}
.hz-m-modal .hz-modal_close .hz-u-icon-close {
font-size: 18px;
transition: transform 500ms ease-in-out;
transform: rotate(0deg);
width: 18px;
text-align: center;
}
.hz-m-modal .hz-modal_close:hover .hz-u-icon-close {
transform: rotate(270deg);
}
.hz-m-modal .hz-modal_bd {
padding: 15px 15px 0 15px;
min-height: 10px;
}
.hz-m-modal .hz-modal_ft {
padding: 15px;
text-align: center;
border-top: 1px solid #f4f4f4;
}
.hz-m-modal .hz-modal_ft .hz-u-btn {
margin: 0 10px;
}
@media (max-width: 767px) {
.hz-m-modal .hz-modal_dialog {
margin: 10px;
}
}
/* 基本按钮样式 btn */
.hz-u-btn {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-appearance: none;
border: none;
overflow: visible;
font: inherit;
text-transform: none;
text-decoration: none;
cursor: pointer;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: none;
display: inline-block;
vertical-align: middle;
text-align: center;
font-size: 12px;
}
.hz-u-btn:hover,
.hz-u-btn:focus {
outline: none;
text-decoration: none;
}
.hz-u-btn:disabled {
cursor: not-allowed;
}
.hz-u-btn-block {
display: block;
width: 100%;
}
.hz-u-btn {
padding: 0 16px;
height: 28px;
line-height: 26px;
background: #f4f4f4;
color: #444;
border: 1px solid #ddd;
-moz-border-radius: 3px;
border-radius: 3px;
}
.hz-u-btn:hover,
.hz-u-btn:focus {
background: #e5e5e5;
border: 1px solid #adadad;
}
.hz-u-btn:active {
background: #e5e5e5;
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.hz-u-btn:disabled {
background: #fff;
border: 1px solid #ccc;
filter: alpha(opacity=65);
opacity: 0.65;
-webkit-box-shadow: none;
box-shadow: none;
}
/* 按钮类型 */
.hz-u-btn-primary {
background: #67739b;
color: #fff;
border: 1px solid #67739b;
}
.hz-u-btn-primary:hover,
.hz-u-btn-primary:focus {
background: #31384b;
color: #fff;
border: 1px solid #31384b;
}
.hz-u-btn-primary:active {
background: #367fa9;
color: #fff;
border: 1px solid #367fa9;
}
.hz-u-btn-primary:disabled {
background: #444;
color: #fff;
border: 1px solid #444;
}
/* input */
.hz-u-input {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
border: 0;
padding: 0;
border-radius: 0;
font: inherit;
color: inherit;
vertical-align: middle;
}
.hz-u-input {
position: relative;
z-index: 0;
padding: 5px 6px;
border: 1px solid #d2d6de;
color: #555;
background: #fff;
-moz-border-radius: 3px;
border-radius: 3px;
}
.hz-u-input::-webkit-input-placeholder {
color: #bbb;
filter: alpha(opacity=100);
opacity: 1;
}
.hz-u-input::-moz-placeholder {
color: #bbb;
filter: alpha(opacity=100);
opacity: 1;
}
.hz-u-input:-moz-placeholder {
color: #bbb;
filter: alpha(opacity=100);
opacity: 1;
}
.hz-u-input:-ms-placeholder {
color: #bbb;
filter: alpha(opacity=100);
opacity: 1;
}
.hz-u-input:focus {
outline: 0;
background: #fff;
color: #555;
border: 1px solid #3c8dbc;
}
.hz-u-input:disabled {
cursor: not-allowed;
background: #eee;
color: #999;
border: 1px solid #d2d6de;
}
.hz-u-input {
width: 280px;
height: 34px;
}
.hz-u-input.hz-u-input-success {
color: #00a65a;
border-color: #00a65a;
}
.hz-u-input.hz-u-input-warning {
color: #f39c12;
border-color: #f39c12;
}
.hz-u-input.hz-u-input-error {
color: #dd4b39;
border-color: #dd4b39;
}
.hz-u-input.hz-u-input-blank {
border-color: transparent;
border-style: dashed;
background: none;
}
.hz-u-input.hz-u-input-blank:focus {
border-color: #ddd;
}
/* formItem */
.hz-u-formitem {
display: inline-block;
*zoom: 1;
margin-bottom: 1em;
}
.hz-u-formitem:before,
.hz-u-formitem:after {
display: table;
content: "";
line-height: 0;
}
.hz-u-formitem:after {
clear: both;
}
.hz-u-formitem .hz-formitem_tt {
display: block;
float: left;
text-align: right;
}
.hz-u-formitem .hz-formitem_ct {
display: block;
}
.hz-u-formitem .hz-formitem_rqr {
line-height: 28px;
color: #dd4b39;
}
.hz-u-formitem .hz-formitem_tt {
line-height: 34px;
width: 100px;
}
.hz-u-formitem .hz-formitem_ct {
line-height: 34px;
margin-left: 108px;
}
/* icon */
.hz-u-icon {
display: inline-block;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* label */
.hz-u-label {
display: inline-block;
cursor: pointer;
}
/* margin */
.hz-f-ml0 {
margin-bottom: 0;
}
/* replicator */
.hz-u-copy input[data-for-copy] {
transform: translateZ(0);
position: fixed;
bottom: 0;
right: 0;
width: 1px;
height: 1px;
opacity: 0;
overflow: hidden;
z-index: -999;
color: transparent;
background-color: transparent;
border: none;
outline: none;
}
@font-face {
font-family: 'iconfont';
/* project id 525460 */
src: url('//at.alicdn.com/t/font_525460_d0ysfwzacahsemi.eot');
src: url('//at.alicdn.com/t/font_525460_d0ysfwzacahsemi.eot?#iefix') format('embedded-opentype'), url('//at.alicdn.com/t/font_525460_d0ysfwzacahsemi.woff') format('woff'), url('//at.alicdn.com/t/font_525460_d0ysfwzacahsemi.ttf') format('truetype'), url('//at.alicdn.com/t/font_525460_d0ysfwzacahsemi.svg#iconfont') format('svg');
}
.hz-icon {
font-family: "iconfont" !important;
font-size: 20px;
font-style: normal;
text-align: center;
user-select: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.hz-icon-edit {
position: absolute;
top: -4px;
left: 50%;
transform: translateX(-50%);
}
.hz-flex-img {
display: flex;
align-items: center;
justify-content: center;
}
.hz-flex-img img {
width: 100%;
height: 100%;
}
.hz-icon-trash:before {
content: "\e605";
}
.hz-edit-img {
width: 100%;
display: flex;
justify-content: center;
}
.hz-edit-img img {
max-width: 300px;
max-height: 200px;
margin-bottom: 10px;
}
.hz-edit-del {
width: 100%;
display: flex;
justify-content: flex-end;
}

View File

@@ -0,0 +1,101 @@
<template>
<div ref="content" class="hz-m-wrap">
<img class="hz-u-img" :src="image" />
<ul class="hz-m-area" v-add-item>
<zone
class="hz-m-item"
v-for="(zone, index) in zones"
:key="index"
:index="index"
:setting="zone"
@delItem="removeItem($event)"
@changeInfo="changeInfo($event)"
></zone>
</ul>
</div>
</template>
<script>
import Zone from "./Zone";
import addItem from "../directives/addItem";
export default {
name: "HotZone",
data() {
return {
zones: [],
};
},
props: {
image: {
type: String,
required: true,
},
zonesInit: {
type: Array,
default: () => [],
},
max: {
type: Number,
},
},
mounted() {
this.zones = this.zonesInit.concat();
},
methods: {
changeInfo(res) {
let { info, index, zoneInfo } = res;
info = { ...zoneInfo, ...info };
// 改变热区并发送change通知
Object.assign(this.zones[index], info);
this.hasChange("changeInfo");
},
addItem(setting) {
this.zones.push(setting);
this.$emit("choose");
// this.hasChange() 不应该发送通知mouseup判定成功才应该发
// this.$emit('add', setting)
},
eraseItem(index = this.zones.length - 1) {
this.zones.splice(index, 1);
this.$emit("erase", index);
},
isOverRange() {
let { max, zones } = this;
return max && zones.length > max;
},
overRange() {
const index = this.zones.length - 1;
this.zones.splice(index, 1);
this.$emit("overRange", index);
},
removeItem(index = this.zones.length - 1) {
this.zones.splice(index, 1);
this.hasChange("removeItem");
this.$emit("remove", index);
},
changeItem(info, isAdd) {
const index = this.zones.length - 1;
// 改变热区并发送change通知
Object.assign(this.zones[index], info);
this.hasChange("changeItem");
isAdd && this.$emit("add", this.zones[index]);
},
hasChange(from) {
this.$emit("change", this.zones);
},
},
directives: {
addItem,
},
components: {
Zone,
},
};
</script>
<style scoped>
@import "../assets/styles/main.css";
</style>

View File

@@ -0,0 +1,212 @@
<template>
<li
v-drag-item
:style="{
top: zoneTop,
left: zoneLeft,
width: zoneWidth,
height: zoneHeight,
}"
>
<ul
v-change-size
class="hz-m-box"
:class="{
'hz-z-hidden': tooSmall,
'hz-m-hoverbox': !hideZone,
}"
>
<li class="hz-u-index" :title="`热区${index + 1}`">{{ index + 1 }}</li>
<li
title="删除该热区"
v-show="!hideZone"
class="hz-u-close hz-icon hz-icon-trash"
@click.prevent.stop="delItem(index)"
@mousedown.stop
@mouseup.stop
@mousemove.stop
></li>
<li
title="编辑该热区"
v-show="!hideZone"
class="hz-u-close hz-icon hz-icon-edit"
@click.prevent.stop="showModalFn(index)"
@mousedown.stop
@mouseup.stop
@mousemove.stop
>
<img width="17" height="17" src="../assets/styles/icons8-edit-64.png"></img>
</li>
<li class="hz-flex-img">
<img class="hz-u-img" :src="zoneForm.img" />
</li>
<li class="hz-u-square hz-u-square-tl" data-pointer="dealTL"></li>
<li class="hz-u-square hz-u-square-tc" data-pointer="dealTC"></li>
<li class="hz-u-square hz-u-square-tr" data-pointer="dealTR"></li>
<li class="hz-u-square hz-u-square-cl" data-pointer="dealCL"></li>
<li class="hz-u-square hz-u-square-cr" data-pointer="dealCR"></li>
<li class="hz-u-square hz-u-square-bl" data-pointer="dealBL"></li>
<li class="hz-u-square hz-u-square-bc" data-pointer="dealBC"></li>
<li class="hz-u-square hz-u-square-br" data-pointer="dealBR"></li>
</ul>
<Modal
v-model="showModal"
title="编辑热区"
draggable
scrollable
:mask="false"
ok-text="保存"
@on-ok="saveZone"
@on-cancel="
() => {
showModal = false;
}
"
>
<div>
<div class="hz-edit-img">
<img class="show-image" :src="zoneForm.img" alt />
</div>
<Form :model="zoneForm" :label-width="80">
<FormItem label="图片链接">
<Input v-model="zoneForm.img"></Input>
<Button size="small" type="primary" @click="handleSelectImg"
>选择图片</Button
>
</FormItem>
<FormItem label="跳转链接">
<Input v-model="zoneForm.link"></Input>
<Button size="small" type="primary" @click="handleSelectLink"
>选择链接</Button
>
</FormItem>
</Form>
</div>
</Modal>
<!-- 选择商品。链接 -->
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
<!-- 选择图片 -->
<Modal width="1200px" v-model="picModelFlag" footer-hide>
<ossManage
@callback="callbackSelected"
:isComponent="true"
ref="ossManage"
/>
</Modal>
</li>
</template>
<script>
import changeSize from "../directives/changeSize";
import dragItem from "../directives/dragItem";
import ossManage from "@/views/sys/oss-manage/ossManage";
export default {
name: "Zone",
components: {
ossManage,
},
data() {
return {
zoneTop: "",
zoneLeft: "",
zoneWidth: "",
zoneHeight: "",
hideZone: false,
tooSmall: false,
showModal: false,
picModelFlag: false,
currentIndex: 0,
zoneForm: {
img: "",
link: "",
type: "",
},
};
},
props: ["index", "setting"],
mounted() {
console.log(this.setting);
this.setZoneInfo(this.setting);
},
methods: {
setZoneInfo(val) {
this.zoneTop = this.getZoneStyle(val.topPer);
this.zoneLeft = this.getZoneStyle(val.leftPer);
this.zoneWidth = this.getZoneStyle(val.widthPer);
this.zoneHeight = this.getZoneStyle(val.heightPer);
this.tooSmall = val.widthPer < 0.01 && val.heightPer < 0.01;
this.zoneForm.img = val.img;
this.zoneForm.link = val.link;
this.zoneForm.type = val.type;
},
handlehideZone(isHide = true) {
if (this.hideZone === isHide) {
return;
}
this.hideZone = isHide;
},
changeInfo(info = {}) {
const { index } = this;
this.$emit("changeInfo", {
info,
index,
zoneInfo: this.zoneForm,
});
},
showModalFn(index) {
this.showModal = true;
this.currentIndex = index;
},
// 选择图片
handleSelectImg() {
this.$refs.ossManage.selectImage = true;
this.picModelFlag = true;
},
// 选择图片回调
callbackSelected(item) {
this.picModelFlag = false;
this.zoneForm.img = item.url;
},
// 调起选择链接弹窗
handleSelectLink(item, index) {
if (item) this.selectedNav = item;
this.$refs.liliDialog.open("link");
},
// 已选链接
selectedLink(val) {
this.zoneForm.link = this.$options.filters.formatLinkType(val);
this.zoneForm.type = val.___type;
},
saveZone() {},
delZone() {
this.delItem(this.currentIndex);
},
delItem(index) {
this.$emit("delItem", index);
},
getZoneStyle(val) {
return `${(val || 0) * 100}%`;
},
},
watch: {
setting: {
handler: function (val) {
this.setZoneInfo(val);
},
deep: true,
},
},
directives: {
changeSize,
dragItem,
},
};
</script>

View File

@@ -0,0 +1,100 @@
import _ from '../utils'
export default {
bind: function (el, binding, vnode) {
const MIN_LIMIT = _.MIN_LIMIT
el.addEventListener('mousedown', handleMouseDown,{ passive: false })
function handleMouseDown (e) {
console.log('additem', e)
e && e.preventDefault()
let itemInfo = {
top: _.getDistanceY(e, el),
left: _.getDistanceX(e, el),
width: 0,
height: 0
}
let container = _.getOffset(el)
// Only used once at the beginning of init
let setting = {
topPer: _.decimalPoint(itemInfo.top / container.height),
leftPer: _.decimalPoint(itemInfo.left / container.width),
widthPer: 0,
heightPer: 0
}
let preX = _.getPageX(e)
let preY = _.getPageY(e)
vnode.context.addItem(setting)// 这里去添加并发送了add通知不应该发送通知
window.addEventListener('mousemove', handleChange,{ passive: false })
window.addEventListener('mouseup', handleMouseUp,{ passive: false })
function handleChange (e) {
e && e.preventDefault()
let moveX = _.getPageX(e) - preX
let moveY = _.getPageY(e) - preY
preX = _.getPageX(e)
preY = _.getPageY(e)
// Not consider the direction of movement first, consider only the lower right drag point
let minLimit = 0
// 添加热区时判定鼠标释放时满足热区大于48*48时条件时生效
let styleInfo = _.dealBR(itemInfo, moveX, moveY, minLimit)
// Boundary value processing 改变热区大小时边界条件的处理
itemInfo = _.dealEdgeValue(itemInfo, styleInfo, container, vnode.context.zones)
Object.assign(el.lastElementChild.style, {
top: `${itemInfo.top}px`,
left: `${itemInfo.left}px`,
width: `${itemInfo.width}px`,
height: `${itemInfo.height}px`
})
}
function handleMouseUp () {
let perInfo = {
topPer: _.decimalPoint(itemInfo.top / container.height),
leftPer: _.decimalPoint(itemInfo.left / container.width),
widthPer: _.decimalPoint(itemInfo.width / container.width),
heightPer: _.decimalPoint(itemInfo.height / container.height),
img: "",
link: "",
type: "",
}
if (vnode.context.isOverRange()) {
vnode.context.overRange() // 判断超出个数限制给overRange钩子抛回调
} else if (container.height < MIN_LIMIT && itemInfo.width > MIN_LIMIT) {
vnode.context.changeItem(Object.assign(perInfo, {
topPer: 0,
heightPer: 1
}), true)
} else if (container.width < MIN_LIMIT && itemInfo.height > MIN_LIMIT) {
vnode.context.changeItem(Object.assign(perInfo, {
leftper: 0,
widthPer: 1
}), true)
} else if (itemInfo.width > MIN_LIMIT && itemInfo.height > MIN_LIMIT) {
vnode.context.changeItem(perInfo, true)
} else {
// 当添加区域超出范围或小于最小区域48*48时触发删除当亲绘制的热区并发送erase事件通知
vnode.context.eraseItem()
}
window.removeEventListener('mousemove', handleChange)
window.removeEventListener('mouseup', handleMouseUp)
}
}
el.$destroy = () => el.removeEventListener('mousedown', handleMouseDown)
},
unbind: function (el) {
el.$destroy()
}
}

View File

@@ -0,0 +1,91 @@
import _ from '../utils'
export default {
bind: function (el, binding, vnode) {
el.addEventListener('mousedown', handleMouseDown,{ passive: false })
function handleMouseDown (e) {
let pointer = e.target.dataset.pointer //元素上绑定的方法名
if (!pointer) {
return
}
e && e.stopPropagation()
let zone = el.parentNode
let setting = vnode.context.setting
let currentIndex = vnode.context.index
let container = _.getOffset(zone.parentNode)
let itemInfo = {
width: _.getOffset(zone).width || 0,
height: _.getOffset(zone).height || 0,
top: setting.topPer * container.height || 0,
left: setting.leftPer * container.width || 0
}
let preX = _.getPageX(e)
let preY = _.getPageY(e)
let flag
// Hide the info displayed by hover
vnode.context.handlehideZone(true)
window.addEventListener('mousemove', handleChange,{ passive: false })
window.addEventListener('mouseup', handleMouseUp,{ passive: false })
function handleChange (e) {
e && e.preventDefault()
flag = true
let moveX = _.getPageX(e) - preX
let moveY = _.getPageY(e) - preY
preX = _.getPageX(e)
preY = _.getPageY(e)
// Handling the situation when different dragging points are selected
let styleInfo = _[pointer](itemInfo, moveX, moveY)//调用对应的方法
// Boundary value processing
itemInfo = _.dealEdgeValue(itemInfo, styleInfo, container, vnode.context.$parent.zones, currentIndex)
Object.assign(zone.style, {
top: `${itemInfo.top}px`,
left: `${itemInfo.left}px`,
width: `${itemInfo.width}px`,
height: `${itemInfo.height}px`
})
}
function handleMouseUp () {
if (flag) {
flag = false
let perInfo = {
topPer: _.decimalPoint(itemInfo.top / container.height),
leftPer: _.decimalPoint(itemInfo.left / container.width),
widthPer: _.decimalPoint(itemInfo.width / container.width),
heightPer: _.decimalPoint(itemInfo.height / container.height)
}
vnode.context.changeInfo(perInfo)
// 兼容数据无变更情况下导致 computed 不更新,数据仍为 px 时 resize 出现的问题
Object.assign(zone.style, {
top: `${itemInfo.top}px`,
left: `${itemInfo.left}px`,
width: `${itemInfo.width}px`,
height: `${itemInfo.height}px`
})
}
// Show the info
vnode.context.handlehideZone(false)
window.removeEventListener('mousemove', handleChange)
window.removeEventListener('mouseup', handleMouseUp)
}
}
el.$destroy = () => el.removeEventListener('mousedown', handleMouseDown)
},
unbind: function (el) {
el.$destroy()
}
}

View File

@@ -0,0 +1,108 @@
import _ from '../utils'
export default {
bind: function (el, binding, vnode) {
el.addEventListener('mousedown', handleMouseDown)
let collision
function handleMouseDown (e) {
e && e.stopPropagation()
let container = _.getOffset(el.parentNode)
let preX = _.getPageX(e)
let preY = _.getPageY(e)
let topPer
let leftPer
let flag
window.addEventListener('mousemove', handleChange,{ passive: false })
window.addEventListener('mouseup', handleMouseUp,{ passive: false })
function handleChange (e) {
e && e.preventDefault()
flag = true
collision = false
// Hide the info displayed by hover
vnode.context.handlehideZone(true)
let setting = vnode.context.setting
let currentIndex = vnode.context.index
let moveX = _.getPageX(e) - preX
let moveY = _.getPageY(e) - preY
setting.topPer = setting.topPer || 0
setting.leftPer = setting.leftPer || 0
topPer = _.decimalPoint(moveY / container.height + setting.topPer)
leftPer = _.decimalPoint(moveX / container.width + setting.leftPer)
// Hotzone moving boundary processing
if (topPer < 0) {
topPer = 0
moveY = -container.height * setting.topPer
}
if (leftPer < 0) {
leftPer = 0
moveX = -container.width * setting.leftPer
}
if (topPer + setting.heightPer > 1) {
topPer = 1 - setting.heightPer
moveY = container.height * (topPer - setting.topPer)
}
if (leftPer + setting.widthPer > 1) {
leftPer = 1 - setting.widthPer
moveX = container.width * (leftPer - setting.leftPer)
}
// 拖拽碰撞检测
if (vnode.context.$parent.zones.length > 1) {
let currentzones = JSON.parse(JSON.stringify(vnode.context.$parent.zones)).map((zone) => {
return {
left: (zone.leftPer || 0) * container.width,
top: (zone.topPer || 0) * container.height,
width: (zone.widthPer || 0) * container.width,
height: (zone.heightPer || 0) * container.height
}
})
// 矫正
let changeSetting = {}
changeSetting.left = setting.leftPer * container.width + moveX
changeSetting.top = setting.topPer * container.height + moveY
changeSetting.width = setting.widthPer * container.width
changeSetting.height = setting.heightPer * container.height
// 碰撞检测
for (let i = 0, len = currentzones.length; i < len; i++) {
if (currentIndex !== i && _.handleEgdeCollisions(currentzones[i], changeSetting)) {
collision = true
break
}
}
}
el.style.transform = `translate(${moveX}px, ${moveY}px)`
}
function handleMouseUp () {
if (flag) {
flag = false
el.style.transform = 'translate(0, 0)'
if (!collision) {
vnode.context.changeInfo({
topPer,
leftPer
})
}
}
// Show the info
vnode.context.handlehideZone(false)
window.removeEventListener('mousemove', handleChange)
window.removeEventListener('mouseup', handleMouseUp)
}
}
el.$destroy = () => el.removeEventListener('mousedown', handleMouseDown)
},
unbind: function (el) {
el.$destroy()
}
}

View File

@@ -0,0 +1,7 @@
import hotzone from './components/Hotzone.vue'
hotzone.install = (Vue) => {
Vue.component(hotzone.name, hotzone)
}
export default hotzone

View File

@@ -0,0 +1,274 @@
let _ = {
MIN_LIMIT: 48, // Min size of zone
DECIMAL_PLACES: 4 // Hotzone positioning decimal point limit number of digits
}
/**
* Get a power result of 10 for the power of the constant
* @return {Number}
*/
_.getMultiple = (decimalPlaces = _.DECIMAL_PLACES) => {
return Math.pow(10, decimalPlaces)
}
/**
* Limit decimal places
* @param {Number} num
* @return {Number}
*/
_.decimalPoint = (val = 0) => { // 处理js小数点计算不精确问题先放再缩小
return Math.round(val * _.getMultiple()) / _.getMultiple() || 0
}
/**
* Get element width and height
* @param {Object} elem
* @return {Object}
*/
_.getOffset = (elem = {}) => ({
width: elem.clientWidth || 0,
height: elem.clientHeight || 0
})
/**
* Get pageX
* @param {Object} e
* @return {Number}
*/
_.getPageX = (e) => ('pageX' in e) ? e.pageX : e.touches[0].pageX
/**
* Get pageY
* @param {Object} e
* @return {Number}
*/
_.getPageY = (e) => ('pageY' in e) ? e.pageY : e.touches[0].pageY
/**
* Gets the abscissa value of the mouse click relative to the target node
* @param {Object} e
* @param {Object} container
* @return {Number}
*/
_.getDistanceX = (e, container) =>
_.getPageX(e) - (container.getBoundingClientRect().left + window.pageXOffset)
/**
* Gets the ordinate value of the mouse click relative to the target node
* @param {Object} e
* @param {Object} container
* @return {Number}
*/
_.getDistanceY = (e, container) =>
_.getPageY(e) - (container.getBoundingClientRect().top + window.pageYOffset)
// 检测区域是否有碰撞 true 有碰撞交集 ,false 无碰撞
_.handleEgdeCollisions = (rect1, rect2) => {
const l1 = { left: rect1.left, top: rect1.top }
const r1 = { left: rect1.left + rect1.width, top: rect1.top + rect1.height }
const l2 = { left: rect2.left, top: rect2.top }
const r2 = { left: rect2.left + rect2.width, top: rect2.top + rect2.height }
return !(
l1.left > r2.left ||
l2.left > r1.left ||
l1.top > r2.top ||
l2.top > r1.top
)
}
/**
* Treatment of boundary conditions when changing the size of the hotzone 改变热区大小时边界条件的处理(如果要避免热区重叠,代码要加载这里)
* @param {Object} itemInfo
* @param {Object} styleInfo
* @param {Object} container
*/
_.dealEdgeValue = (itemInfo, styleInfo, container, zones, currentIndex = zones.length - 1) => {
if (Object.prototype.hasOwnProperty.call(styleInfo, "left") && styleInfo.left < 0) {
styleInfo.left = 0
styleInfo.width = itemInfo.width + itemInfo.left
}
if (Object.prototype.hasOwnProperty.call(styleInfo, "top") && styleInfo.top < 0) {
styleInfo.top = 0
styleInfo.height = itemInfo.height + itemInfo.top
}
if (!Object.prototype.hasOwnProperty.call(styleInfo, "left") && Object.prototype.hasOwnProperty.call(styleInfo, "width")) {
if (itemInfo.left + styleInfo.width > container.width) {
styleInfo.width = container.width - itemInfo.left
}
}
if (!Object.prototype.hasOwnProperty.call(styleInfo, "top") && Object.prototype.hasOwnProperty.call(styleInfo, "height")) {
if (itemInfo.top + styleInfo.height > container.height) {
styleInfo.height = container.height - itemInfo.top
}
}
// 与其他热区重叠,则修正 检测是否发生碰撞
if (zones.length > 1) {
let currentzones = JSON.parse(JSON.stringify(zones)).map((zone) => {
return {
left: (zone.leftPer || 0) * container.width,
top: (zone.topPer || 0) * container.height,
width: (zone.widthPer || 0) * container.width,
height: (zone.heightPer || 0) * container.height
}
})
let current = { ...itemInfo, ...styleInfo }
for (let i = 0, len = currentzones.length; i < len; i++) {
if (currentIndex !== i && _.handleEgdeCollisions(currentzones[i], current)) {
return itemInfo
}
}
}
return Object.assign(itemInfo, styleInfo)
}
/**
* Handle different drag points, capital letters mean: T-topL-leftC-centerR-rightB-bottom
* @param {Object} itemInfo
* @param {Number} moveX
* @param {Number} moveY
* @return {Object}
*/
_.dealTL = (itemInfo, moveX, moveY, minLimit = _.MIN_LIMIT) => {
let styleInfo = {}
let width = itemInfo.width - moveX
let height = itemInfo.height - moveY
if (width >= Math.min(minLimit, itemInfo.width)) {
Object.assign(styleInfo, {
width,
left: itemInfo.left + moveX
})
}
if (height >= Math.min(minLimit, itemInfo.height)) {
Object.assign(styleInfo, {
height,
top: itemInfo.top + moveY
})
}
return styleInfo
}
_.dealTC = (itemInfo, moveX, moveY, minLimit = _.MIN_LIMIT) => {
let styleInfo = {}
let height = itemInfo.height - moveY
if (height >= Math.min(minLimit, itemInfo.height)) {
styleInfo = {
height,
top: itemInfo.top + moveY
}
}
return styleInfo
}
_.dealTR = (itemInfo, moveX, moveY, minLimit = _.MIN_LIMIT) => {
let styleInfo = {}
let width = itemInfo.width + moveX
let height = itemInfo.height - moveY
if (width >= Math.min(minLimit, itemInfo.width)) {
Object.assign(styleInfo, {
width
})
}
if (height >= Math.min(minLimit, itemInfo.height)) {
Object.assign(styleInfo, {
height,
top: itemInfo.top + moveY
})
}
return styleInfo
}
_.dealCL = (itemInfo, moveX, moveY, minLimit = _.MIN_LIMIT) => {
let styleInfo = {}
let width = itemInfo.width - moveX
if (width >= Math.min(minLimit, itemInfo.width)) {
Object.assign(styleInfo, {
width,
left: itemInfo.left + moveX
})
}
return styleInfo
}
_.dealCR = (itemInfo, moveX, moveY, minLimit = _.MIN_LIMIT) => {
let styleInfo = {}
let width = itemInfo.width + moveX
if (width >= Math.min(minLimit, itemInfo.width)) {
Object.assign(styleInfo, {
width
})
}
return styleInfo
}
_.dealBL = (itemInfo, moveX, moveY, minLimit = _.MIN_LIMIT) => {
let styleInfo = {}
let width = itemInfo.width - moveX
let height = itemInfo.height + moveY
if (width >= Math.min(minLimit, itemInfo.width)) {
Object.assign(styleInfo, {
width,
left: itemInfo.left + moveX
})
}
if (height >= Math.min(minLimit, itemInfo.height)) {
Object.assign(styleInfo, {
height
})
}
return styleInfo
}
_.dealBC = (itemInfo, moveX, moveY, minLimit = _.MIN_LIMIT) => {
let styleInfo = {}
let height = itemInfo.height + moveY
if (height >= Math.min(minLimit, itemInfo.height)) {
Object.assign(styleInfo, {
height
})
}
return styleInfo
}
// 添加热区时,判定鼠标释放点满足一下条件时生效
_.dealBR = (itemInfo, moveX, moveY, minLimit = _.MIN_LIMIT) => {
let styleInfo = {}
let width = itemInfo.width + moveX
let height = itemInfo.height + moveY
if (width >= Math.min(minLimit, itemInfo.width)) {
// 改变后的宽度 >= min(之前宽度,内置的最小宽度标准),即生效
Object.assign(styleInfo, {
width
})
}
if (height >= Math.min(minLimit, itemInfo.height)) {
// 改变后的高度 大于等于 Min最小高度之前高度生效
Object.assign(styleInfo, {
height
})
}
return styleInfo
}
export default _

View File

@@ -0,0 +1,55 @@
<template>
<div>
<Button @click="enable = true">语言设定</Button>
<Modal v-model="enable" draggable sticky scrollable :mask="false" :title="title">
<Tabs closable type="card" @on-tab-remove="handleTabRemove" :value="language[0].title">
<TabPane v-for="(item,index) in language" :key="index" :label="item.title" :name="item.title">
<Input v-model="item.___i18n" />
</TabPane>
</Tabs>
</Modal>
</div>
</template>
<script>
import {language} from "./languages";
export default {
/**
* tabs 循环的语言内容格式 [{'title':'test','value':'val'}]
*/
props: {
value: {
type: null,
default: "",
},
},
watch: {
language: {
handler(val) {
this.$emit("language", { language: [...val], val: this.value });
},
deep: true,
},
},
data() {
return {
language,
tabVal: "",
enable: false, //是否开启modal
title: "转换语言",
};
},
methods: {
// 删除tab标签将没有用的语音进行删除
handleTabRemove(tab) {
this.language = this.language.filter((item) => {
return item.value != tab;
});
},
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1 @@
export const language = [{ title: "中文",value:1 }, { title: "英文" ,value:2}];

View File

@@ -0,0 +1,249 @@
<template>
<div class="wrapper">
<div class="wap-content">
<div class="query-wrapper">
<div class="query-item">
<div>搜索范围</div>
<Input placeholder="商品名称" @on-clear="goodsData=[]; goodsParams.goodsName=''; goodsParams.pageNumber = 1; getQueryGoodsList()" @on-enter="()=>{goodsData=[];goodsParams.pageNumber =1; getQueryGoodsList();}" icon="ios-search" clearable
style="width: 150px" v-model="goodsParams.goodsName" />
</div>
<div class="query-item">
<Cascader v-model="category" placeholder="请选择商品分类" style="width: 250px" :data="skuList"></Cascader>
</div>
<div class="query-item">
<Button type="primary" @click="goodsData=[]; getQueryGoodsList();" icon="ios-search">搜索</Button>
</div>
</div>
<div style="positon:retavle;">
<Scroll class="wap-content-list" :on-reach-bottom="handleReachBottom" :distance-to-edge="[3,3]">
<div class="wap-content-item" :class="{ active: item.selected }" @click="checkedGoods(item, index)" v-for="(item, index) in goodsData" :key="index">
<div>
<img :src="item.thumbnail" alt="" />
</div>
<div class="wap-content-desc">
<div class="wap-content-desc-title">{{ item.goodsName }}</div>
<div class="wap-sku">{{ item.goodsUnit }}</div>
<div class="wap-sku"><Tag :color="item.salesModel === 'RETAIL' ? 'default' : 'geekblue'">{{item.salesModel === "RETAIL" ? "零售型" : "批发型"}}</Tag></div>
<div class="wap-content-desc-bottom">
<div>¥{{ item.price | unitPrice }}</div>
</div>
</div>
</div>
<Spin size="large" fix v-if="loading"></Spin>
<div v-if="empty" class="empty">暂无商品信息</div>
</Scroll>
</div>
</div>
</div>
</template>
<script>
import * as API_Goods from "@/api/goods";
export default {
data() {
return {
type: "multiple", //单选或者多选 single multiple
skuList: [], // 商品sku列表
total: 0, // 商品总数
goodsParams: { // 商品请求参数
pageNumber: 1,
pageSize: 18,
order: "desc",
goodsName: "",
sn: "",
categoryPath: "",
marketEnable: "UPPER",
authFlag: "PASS",
sort:"createTime"
},
category: [], // 分类
goodsData: [], // 商品数据
empty: false, // 空数据
loading: false, // 加载状态
};
},
props: {
selectedWay: {
type: Array,
default: function(){
return new Array()
}
}
},
watch: {
category(val) {
this.goodsParams.categoryPath = val[2];
},
selectedWay: {
handler() {
this.$emit("selected", this.selectedWay);
},
deep: true,
immediate: true
},
"goodsParams.categoryPath": {
handler: function () {
this.goodsData = [];
(this.goodsParams.pageNumber = 0), this.getQueryGoodsList();
},
deep: true,
},
},
mounted() {
this.init();
},
methods: {
// 触底加载更多方法
handleReachBottom() {
setTimeout(() => {
if (
this.goodsParams.pageNumber * this.goodsParams.pageSize <=
this.total
) {
this.goodsParams.pageNumber++;
this.getQueryGoodsList();
}
}, 1500);
},
// 获取商品列表
getQueryGoodsList() {
API_Goods.getGoodsSkuData(this.goodsParams).then((res) => {
this.initGoods(res);
});
},
// 获取列表方法
initGoods(res) {
if (res.result.records.length !=0) {
res.result.records.forEach((item) => {
item.selected = false;
item.___type = "goods"; //设置为goods让pc wap知道标识
this.selectedWay.forEach(e => {
if (e.id && e.id === item.id) {
item.selected = true
}
})
});
/**
* 解决数据请求中,滚动栏会一直上下跳动
*/
this.total = res.result.total;
this.goodsData.push(...res.result.records);
} else {
this.empty = true;
}
},
// 查询商品
init() {
API_Goods.getGoodsSkuData(this.goodsParams).then((res) => {
// 商品
this.initGoods(res);
});
if (localStorage.getItem('category')) {
this.deepGroup(JSON.parse(localStorage.getItem('category')))
} else {
setTimeout(() => {
this.deepGroup(JSON.parse(localStorage.getItem('category')))
},3000)
}
},
deepGroup(val) {
val.forEach((item) => {
let childWay = []; //第二级
// 第二层
if (item.children) {
item.children.forEach((child) => {
// // 第三层
if (child.children) {
child.children.forEach((grandson, index, arr) => {
arr[index] = {
value: grandson.id,
label: grandson.name,
children: "",
};
});
}
let children = {
value: child.id,
label: child.name,
children: child.children,
};
childWay.push(children);
});
}
// 第一层
let way = {
value: item.id,
label: item.name,
children: childWay,
};
this.skuList.push(way);
});
},
/**
* 点击商品
*/
checkedGoods(val, index) {
// 如果单选的话
if (this.type != "multiple") {
this.goodsData.forEach((item) => {
item.selected = false;
});
this.selectedWay = [];
val.selected = true;
this.selectedWay.push(val);
return false;
}
if (val.selected == false) {
val.selected = true;
this.selectedWay.push(val);
} else {
val.selected = false;
for (let i = 0; i<this.selectedWay.length; i++ ) {
if (this.selectedWay[i].id===val.id) {
this.selectedWay.splice(i,1)
break;
}
}
}
},
},
};
</script>
<style scoped lang="scss">
@import "./style.scss";
.wap-content {
width: 100%;
}
.empty {
text-align: center;
padding: 8px 0;
width: 100%;
}
.wap-content {
flex: 1;
padding: 0;
}
.wap-content-list {
position: relative;
}
.wap-content-item {
width: 210px;
margin: 10px 7px;
padding: 6px 0;
}
// .wap-content-item{
// }
.active {
background: url("../../assets/selected.png") no-repeat;
background-position: right;
background-size: 10%;
}
</style>

View File

@@ -0,0 +1,83 @@
<template>
<Modal :styles="{ top: '120px' }" width="1160" @on-cancel="clickClose" @on-ok="clickOK" v-model="flag" :mask-closable="false" scrollable>
<template v-if="flag">
<goodsDialog @selected="(val) => {goodsData = val;}"
v-if="goodsFlag" ref="goodsDialog" :selectedWay='goodsData'/>
<linkDialog @selectedLink="(val) => { linkData = val; }" v-else class="linkDialog" />
</template>
</Modal>
</template>
<script>
import goodsDialog from "./goods-dialog";
import linkDialog from "./link-dialog";
export default {
components: {
goodsDialog,
linkDialog
},
data() {
return {
goodsFlag: false, // 是否商品选择器
goodsData: [], //选择的商品
linkData: "", //选择的链接
flag: false, // modal显隐
};
},
methods: {
clearGoodsSelected(){
this.goodsData = []
},
// 关闭弹窗
clickClose() {
this.$emit("closeFlag", false);
this.goodsFlag = false;
},
// 单选商品
singleGoods() {
var timer = setInterval(() => {
if (this.$refs.goodsDialog) {
this.$refs.goodsDialog.type = "single";
clearInterval(timer);
}
}, 100);
},
// 点击确认
clickOK() {
if (this.goodsFlag) {
this.$emit("selectedGoodsData", this.goodsData);
} else {
this.$emit("selectedLink", this.linkData);
}
this.clickClose();
},
// 打开组件方法
open(type, mutiple) {
this.flag = true;
if (type == "goods") {
this.goodsFlag = true;
if (mutiple) {
this.singleGoods()
}
} else {
this.goodsFlag = false;
}
},
// 关闭组件
close() {
this.flag = false;
},
},
};
</script>
<style scoped lang="scss">
/deep/ .ivu-modal {
overflow: hidden;
height: 650px !important;
}
/deep/ .ivu-modal-body {
width: 100%;
height: 500px;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,85 @@
<template>
<div class="wrapper">
<Tabs :value="wap[0].title" class="tabs">
<TabPane
:label="item.title"
:name="item.title"
@click="clickTag(item, i)"
v-for="(item, i) in wap"
:key="i"
>
<component
ref="lili-component"
:is="templateWay[item.name]"
@selected="
(val) => {
changed = val;
}
"
/>
</TabPane>
</Tabs>
</div>
</template>
<script>
import wap from "./wap.js";
import goodsDialog from "./goods-dialog";
import templateWay from "./template/index";
export default {
components: {
goodsDialog,
},
data() {
return {
templateWay, // 模板数据
changed: "", // 变更模板
selected: 0, // 已选数据
selectedLink: "", //选中的链接
wap, // tab标签
};
},
watch: {
changed: {
handler(val) {
this.$emit("selectedLink", val[0]); //因为是单选,所以直接返回第一个
},
deep: true,
},
},
mounted() {
this.$nextTick(() => {
this.$refs["lili-component"][0].type = "single"; //商品页面设置成为单选
});
this.wap.forEach((item) => {
if (item) {
item.selected = false;
}
});
},
methods: {},
};
</script>
<style scoped lang="scss">
@import "./style.scss";
.wap-content-list {
display: flex;
flex-wrap: wrap;
}
.wap-flex {
margin: 2px;
}
.tabs {
width: 100%;
}
/deep/ .ivu-modal {
overflow: hidden;
height: 650px !important;
}
/deep/ .ivu-modal-body {
width: 100%;
height: 500px;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,112 @@
.wrapper {
width: 100%;
height: 100%;
display: flex;
.wap-list {
flex: 2;
text-align: center;
overflow-y: auto;
height: 100%;
}
>.wap-list,
.wap-content {
flex: 8;
}
}
.wap-sku {
font-size: 12px;
color: #999;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.query-wrapper {
display: flex;
margin: 8px 0;
>.query-item {
display: flex;
align-items: center;
>* {
margin: 0 4px;
}
}
}
/deep/ .ivu-scroll-container {
width: 100% !important;
height: 400px !important;
}
/deep/ .ivu-scroll-content {
/* */
display: flex;
flex-wrap: wrap;
}
.wap-content-list {
overflow-y: auto;
}
.wap-item {
padding: 10px 0;
cursor: pointer;
}
.wap-item:hover {
background: #ededed;
}
.wap-content-item {
cursor: pointer;
display: flex;
height: 100px;
padding: 2px;
overflow: hidden;
align-items: center;
margin: 10px;
/deep/ img {
width: 60px;
height: 60px;
text-align: center;
}
.wap-content-desc {
width: 180px;
padding: 8px;
>.wap-content-desc-title {
display: -webkit-box;
font-size: 12px;
color: #666;
-webkit-box-orient: vertical;
overflow: hidden;
-webkit-line-clamp: 2;
}
>.wap-content-desc-bottom {
font-size: 12px;
padding: 4px 0;
color: #999;
display: flex;
justify-content: space-between;
>div:nth-of-type(1) {
color: $theme_color;
}
}
}
}

View File

@@ -0,0 +1,118 @@
<template>
<div class="wrapper">
<!-- 一级分类 -->
<div class="list">
<div class="list-item" :class="{active:parentIndex === cateIndex}" @click="handleClickChild(cate,cateIndex)" v-for="(cate,cateIndex) in categoryList" :key="cateIndex">
{{cate.name}}
</div>
</div>
<!-- 二级分类 -->
<div class="list">
<div class="list-item" :class="{active:secondIndex === secondI}" @click="handleClickSecondChild(second,secondI)" v-if="secondLevel.length != 0" v-for="(second,secondI) in secondLevel"
:key="secondI">
{{second.name}}
</div>
</div>
<!--三级分类 -->
<div class="list">
<div class="list-item" :class="{active:thirdIndex === thirdI}" @click="handleClickthirdChild(third,thirdI)" v-if="thirdLevel.length != 0" v-for="(third,thirdI) in thirdLevel" :key="thirdI">
{{third.name}}
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
parentIndex: '', // 分类父级下标
secondIndex: '', // 分类二级下标
thirdIndex: '', // 分类三级下标
categoryList: [], // 分类列表一级数据
secondLevel: [], // 分类列表二级数据
thirdLevel: [], // 分类列表三级数据
selectedData: "", // 已选分类数据
};
},
mounted() {
this.init();
},
methods: {
// 点击一级
handleClickChild(item, index) {
this.parentIndex = index;
this.secondLevel = item.children;
item.___type = "category";
item.allId = item.id;
this.secondIndex = '';
this.thirdIndex = '';
this.thirdLevel = []
this.$emit("selected", [item]);
// 点击第一级的时候默认显示第二级第一个
// this.handleClickSecondChild(item.children, 0);
},
// 点击二级
handleClickSecondChild(second, index) {
second.___type = "category";
second.allId = `${second.parentId},${second.id}`
this.secondIndex = index;
this.thirdLevel = second.children;
this.thirdIndex = '';
this.$emit("selected", [second]);
// this.handleClickthirdChild(second.children[0], 0);
},
// 点击三级
handleClickthirdChild(item, index) {
item.___type = "category";
item.allId = `${this.categoryList[this.parentIndex].id},${item.parentId},${item.id}`
this.$emit("selected", [item]);
this.thirdIndex = index;
},
init() {
let category = JSON.parse(localStorage.getItem('category'))
if (category) {
category.forEach((item) => {
item.___type = "category";
});
this.categoryList = category;
// this.handleClickChild(category[0], 0);
} else {
setTimeout(() => {
category = JSON.parse(localStorage.getItem('category'))
category.forEach((item) => {
item.___type = "category";
});
this.categoryList = category;
// this.handleClickChild(category[0], 0);
},3000)
}
},
},
};
</script>
<style lang="scss" scoped>
.list {
width: 30%;
margin: 0 1.5%;
height: 400px;
overflow: auto;
> .list-item {
padding: 10px;
transition: 0.35s;
cursor: pointer;
}
.list-item:hover {
background: #ededed;
}
}
.active {
background: #ededed;
}
.wrapper {
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,15 @@
import category from './category.vue'
import shops from './shops.vue'
import marketing from './marketing.vue'
import pages from './pages.vue'
import goods from '../goods-dialog.vue'
import other from './other.vue'
export default {
pages,
marketing,
shops,
category,
goods,
other,
}

View File

@@ -0,0 +1,460 @@
<template>
<div class="wrapper">
<div class="list">
<div
class="list-item"
v-for="(item, index) in Object.keys(promotionList)"
:key="index"
@click="clickPromotion(item, index)"
:class="{ active: selectedIndex == index }"
>
{{ typeOption(item).title }}
</div>
<!-- <div class="list-item" >暂无活动</div> -->
</div>
<div class="content">
<div v-if="showPromotionList">
<!-- <div class="search-views">
<Input v-model="value11" disabled class="search">
<span slot="prepend">店铺名称</span>
</Input>
<Button type="primary">选择</Button>
</div> -->
<div class="tables">
<Table
height="350"
border
tooltip
:loading="loading"
:columns="activeColumns"
:data="showPromotionList"
></Table>
<Page
@on-change="
(val) => {
params.pageNumber = val;
}
"
:current="params.pageNumber"
:page-size="params.pageSize"
class="mt_10"
:total="Number(totals)"
size="small"
show-elevator
/>
</div>
</div>
</div>
</div>
</template>
<script>
import {
getAllPromotion,
getPromotionSeckill,
getPromotionGoods,
} from "@/api/promotion";
export default {
data() {
return {
totals: "", // 总数
loading: true, //表格请求数据为true
promotionList: "", // 活动列表
selectedIndex: 0, //左侧菜单选择
promotions: "", //选中的活动key
index: 999, // 已选下标
params: {
// 请求参数
pageNumber: 1,
pageSize: 10,
},
pintuanColumns: [
// 表头
{
title: "活动标题",
key: "title",
tooltip: true,
width: 250,
},
{
title: "商品名称",
key: "goodsName",
tooltip: true,
},
{
title: "店铺名称",
key: "storeName",
tooltip: true,
},
{
title: "开始时间",
key: "startTime",
tooltip: true,
},
{
title: "结束时间",
key: "endTime",
tooltip: true,
},
{
title: "操作",
key: "action",
fixed: "right",
width: 100,
render: (h, params) => {
return h("div", [
h(
"Button",
{
props: {
// type: this.index == params.index ? "primary" : "",
size: "small",
},
on: {
click: () => {
this.selectedPromotion(params);
},
},
},
this.index == params.index ? "已选" : "选择"
),
]);
},
},
],
seckillColumns: [
{
title: "商品名称",
key: "goodsName",
tooltip: true,
width: 200,
},
{
title: "店铺名称",
key: "storeName",
tooltip: true,
},
{
title: "活动时间",
key: "timeLine",
tooltip: true,
render: (h, params) => {
return h("div", {}, `${params.row.timeLine}点`);
},
},
{
title: "原价",
key: "originalPrice",
tooltip: true,
render: (h, params) => {
return h(
"div",
{},
this.$options.filters.unitPrice(params.row.originalPrice)
);
},
},
{
title: "现价",
key: "price",
tooltip: true,
render: (h, params) => {
return h(
"div",
{
style: {},
},
this.$options.filters.unitPrice(params.row.price, "")
);
},
},
{
title: "状态",
key: "promotionApplyStatus",
tooltip: true,
render: (h, params) => {
return h(
"div",
{
style: {},
},
params.row.promotionApplyStatus == "APPLY"
? "申请"
: params.row.promotionApplyStatus == "PASS"
? "通过"
: "拒绝"
);
},
},
{
title: "操作",
key: "action",
width: 100,
fixed: "right",
render: (h, params) => {
return h("div", [
h(
"Button",
{
props: {
// type: this.index == params.index ? "primary" : "",
size: "small",
},
on: {
click: () => {
this.selectedPromotion(params);
},
},
},
this.index == params.index ? "已选" : "选择"
),
]);
},
},
],
activeColumns: [], // 活动表头
columns: [
{
title: "活动标题",
key: "title",
tooltip: true,
width: 200,
},
{
title: "商品名称",
key: "goodsName",
tooltip: true,
},
{
title: "活动开始时间",
key: "startTime",
tooltip: true,
},
{
title: "活动结束时间",
key: "endTime",
tooltip: true,
},
{
title: "操作",
key: "action",
fixed: "right",
width: 100,
render: (h, params) => {
return h("div", [
h(
"Button",
{
props: {
// type: this.index == params.index ? "primary" : "",
size: "small",
},
on: {
click: () => {
this.selectedPromotion(params);
},
},
},
this.index == params.index ? "已选" : "选择"
),
]);
},
},
],
promotionData: "", //商品集合
showPromotionList: [], //显示当前促销的商品
};
},
mounted() {
this.init();
},
watch: {
params: {
handler() {
this.index = 999;
this.typeOption(this.promotions) &&
this.typeOption(this.promotions).methodsed();
},
deep: true,
},
},
methods: {
sortGoods(type) {
this.loading = false;
this.params.pageNumber - 1;
this.showPromotionList = this.promotionList[type];
},
typeOption(type) {
// 活动选项
switch (type) {
case "FULL_DISCOUNT":
return {
title: "满减",
methodsed: () => {
this.showPromotionList = [];
this.activeColumns = this.pintuanColumns;
this.sortGoods("FULL_DISCOUNT");
},
};
case "PINTUAN":
return {
title: "拼团",
methodsed: (id) => {
this.showPromotionList = [];
this.activeColumns = this.pintuanColumns;
this.sortGoods("PINTUAN");
},
};
case "KANJIA":
return {
title: "砍价",
methodsed: (id) => {
this.showPromotionList = [];
this.activeColumns = this.pintuanColumns;
this.sortGoods("KANJIA");
},
};
case "SECKILL":
return {
title: "秒杀",
methodsed: () => {
this.showPromotionList = [];
this.activeColumns = this.seckillColumns;
this.sortGoods("SECKILL");
},
};
case "COUPON":
return {
title: "优惠券",
methodsed: () => {
this.showPromotionList = [];
this.activeColumns = this.pintuanColumns;
this.sortGoods("COUPON");
},
};
case "POINTS_GOODS":
return {
title: "积分商品",
methodsed: () => {
this.showPromotionList = [];
this.activeColumns = this.pintuanColumns;
this.sortGoods("POINTS_GOODS");
},
};
default:
return {};
}
},
// 选择活动
selectedPromotion(val) {
val.row.___type = "marketing";
val.row.___promotion = this.promotions;
this.$emit("selected", [val.row]);
this.index = val.index;
},
// 获取所有营销的活动
async init() {
let res = await getAllPromotion();
if (res.success) {
this.loading = false;
this.getPromotion(res);
// this.clickPromotion(this.typeOption[Object.keys(res.result)[0]], 0);
} else {
this.loading = false;
}
},
getPromotion(res) {
if (res.result) {
this.promotionList = res.result;
this.typeOption(Object.keys(res.result)[0]).methodsed();
}
// if (Object.keys(res.result).length) {
// this.typeOption[Object.keys(res.result)[0]].methodsed(
// this.promotionList[Object.keys(res.result)[0]].id
// );
// }
},
// 点击某个活动查询活动列表
clickPromotion(val, i) {
this.promotions = val;
this.selectedIndex = i;
this.params.pageNumber = 1;
this.typeOption(val) &&
this.typeOption(val).methodsed(this.promotionList[val].id);
},
},
};
</script>
<style lang="scss" scoped>
img {
max-width: 100% !important;
}
.search {
width: 300px;
}
.page {
margin-top: 2vh;
text-align: right;
}
.time {
font-size: 12px;
}
.tables {
height: 400px;
margin-top: 20px;
overflow: auto;
width: 100%;
}
/deep/ .ivu-table-wrapper {
width: 100%;
}
.list {
margin: 0 1.5%;
height: 400px;
overflow: auto;
> .list-item {
padding: 10px;
transition: 0.35s;
cursor: pointer;
}
.list-item:hover {
background: #ededed;
}
}
.list {
flex: 1;
width: auto;
}
.content {
overflow: hidden;
flex: 4;
}
.active {
background: #ededed;
}
.wrapper {
overflow: hidden;
}
.search-views {
display: flex;
> * {
margin: 0 4px;
}
}
</style>

View File

@@ -0,0 +1,145 @@
<template>
<div>
<Row :gutter="30">
<Col
span="4"
v-for="(item, index) in linkList"
:key="index"
v-if="
(item.title !== '拼团频道' && item.title !== '签到') ||
$route.name !== 'renovation'
"
>
<div
class="card"
:class="{ active: selectedIndex == index }"
@click="handleLink(item, index)"
>
<Icon size="24" :type="item.icon" />
<p>{{ item.title }}</p>
</div>
</Col>
<!-- 外部链接只有pc端跳转 -->
<Col span="4">
<div
class="card"
:class="{ active: selectedIndex == linkList.length }"
@click="handleLink(linkItem, linkList.length)"
>
<Icon size="24" :type="linkItem.icon" />
<p>{{ linkItem.title }}</p>
</div>
</Col>
</Row>
</div>
</template>
<script>
export default {
data() {
return {
linkList: [
// 链接列表
{
title: "首页",
icon: "md-home",
___type: "home",
},
{
title: "购物车",
icon: "md-cart",
___type: "cart",
},
{
title: "收藏商品",
icon: "md-heart",
___type: "collection",
},
{
title: "我的订单",
icon: "md-document",
___type: "order",
},
{
title: "个人中心",
icon: "md-person",
___type: "user",
},
{
title: "拼团频道",
icon: "md-flame",
___type: "group",
},
{
title: "秒杀频道",
icon: "md-flame",
___type: "seckill",
},
{
title: "领券中心",
icon: "md-pricetag",
___type: "coupon",
},
{
title: "签到",
icon: "md-happy",
___type: "sign",
},
{
title: "小程序直播",
icon: "ios-videocam",
___type: "live",
},
{
title: "砍价",
icon: "md-share-alt",
___type: "kanjia",
},
{
title: "积分商城",
icon: "ios-basket",
___type: "point",
},
],
linkItem: {
title: "外部链接",
icon: "ios-link",
___type: "link",
url: "",
},
linkVisible: false, // 是否显示外部链接
selectedIndex: 9999999, // 已选index
};
},
methods: {
handleLink(val, index) {
val = { ...val, ___type: "other" };
this.selectedIndex = index;
this.$emit("selected", [val]);
},
},
};
</script>
<style lang="scss" scoped>
@import "../style.scss";
.card {
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
padding: 20px 0;
margin: 10px 0;
text-align: center;
transition: 0.35s;
cursor: pointer;
/deep/ p {
margin: 10px 0;
}
border: 1px solid #ededed;
}
.card:hover {
background: #ededed;
}
.active {
background: #ededed;
}
</style>

View File

@@ -0,0 +1,80 @@
<template>
<div class="wrapper">
<!-- TODO 目前数据少暂且不用 -->
<!-- <div class="list">
<div class="list-item active">
文章页
</div>
</div> -->
<div class="content">
<Article @callbacked="callbackArticle" :selected="true" />
</div>
</div>
</template>
<script>
import Article from "@/views/page/article-manage/articleList.vue";
export default {
components: {
Article,
},
data() {
return {};
},
methods: {
callbackArticle(val) {
val.___type = "pages";
val.___path = "/pages/passport/article";
this.$emit("selected", [val]);
},
},
};
</script>
<style lang="scss" scoped>
/deep/ .ivu-card-body {
height: 414px;
overflow: auto;
}
.ivu-table-wrapper ivu-table-wrapper-with-border {
height: 300px !important;
}
.list {
margin: 0 1.5%;
height: 400px;
overflow: auto;
> .list-item {
padding: 10px;
transition: 0.35s;
cursor: pointer;
}
.list-item:hover {
background: #ededed;
}
}
.list {
flex: 2;
width: auto;
}
.content {
overflow: hidden;
flex: 8;
height: 431px;
}
.active {
background: #ededed;
}
.wrapper {
height: 416px;
overflow: hidden;
}
/deep/ .ivu-table {
height: 300px !important;
overflow: auto;
}
/deep/ .ivu-card-body {
padding: 0;
height: auto;
}
</style>

View File

@@ -0,0 +1,110 @@
<template>
<div class="shop">
<div class="wap-content">
<div class="query-wrapper">
<div class="query-item">
<div>店铺名称</div>
<Input placeholder="请输入店铺名称" @on-clear="shopsData=[]; params.storeName=''; params.pageNumber =1; init()" @on-enter="()=>{shopsData=[]; params.pageNumber =1; init();}" icon="ios-search" clearable style="width: 150px"
v-model="params.storeName" />
</div>
<div class="query-item">
<Button type="primary" @click="shopsData=[];params.pageNumber =1; init();" icon="ios-search">搜索</Button>
</div>
</div>
<div>
<Scroll class="wap-content-list" :on-reach-bottom="handleReachBottom" :distance-to-edge="23">
<div class="wap-content-item" @click="clickShop(item,index)" :class="{ active:selected == index }" v-for="(item, index) in shopsData" :key="index">
<div>
<img class="shop-logo" :src="item.storeLogo" alt="" />
</div>
<div class="wap-content-desc">
<div class="wap-content-desc-title">{{ item.storeName }}</div>
<div class="self-operated" :class="{'theme_color':item.selfOperated }">{{ item.selfOperated ? '自营' : '非自营' }}</div>
<div class="wap-sku" :class="{'theme_color':(item.storeDisable === 'OPEN' ? true : false) }">{{ item.storeDisable === 'OPEN' ? '开启中' : '未开启' }}</div>
</div>
</div>
<Spin size="large" fix v-if="loading"></Spin>
</Scroll>
</div>
</div>
</div>
</template>
<script>
import { getShopListData } from "@/api/shops.js";
export default {
data() {
return {
loading: false, // 加载状态
total: "", // 总数
params: { // 请求参数
pageNumber: 1,
pageSize: 10,
storeDisable: "OPEN",
storeName: "",
},
shopsData: [], // 店铺数据
selected: 9999999999, //设置一个不可能选中的index
};
},
watch: {},
created() {
this.init();
},
methods: {
handleReachBottom() {
setTimeout(() => {
if (this.params.pageNumber * this.params.pageSize <= this.total) {
this.params.pageNumber++;
this.init();
}
}, 1500);
},
init() {
this.loading = true;
getShopListData(this.params).then((res) => {
if (res.success) {
/**
* 解决数据请求中,滚动栏会一直上下跳动
*/
this.total = res.result.total;
this.shopsData.push(...res.result.records);
this.loading = false;
}
});
},
clickShop(val, i) {
this.selected = i;
val = { ...val, ___type: "shops" };
this.$emit("selected", [val]);
},
},
};
</script>
<style lang="scss" scoped>
@import "../style.scss";
.shop {
display: flex;
}
.self-operated {
font-size: 12px;
color: #999;
}
.wap-content-list {
flex-wrap: wrap;
}
.shop-logo {
object-fit: cover;
}
.wap-content-item {
}
.active {
background: url("../../../assets/selected.png") no-repeat;
background-position: right;
background-size: 10%;
}
</style>

View File

@@ -0,0 +1,33 @@
export default [
{
title: "商品",
url: "0",
openGoods: true,
name: "goods"
},
{
title: "分类",
url: "1",
name: "category"
},
{
title: "店铺",
url: "2",
name: "shops"
},
{
title: "活动",
url: "3",
name: "marketing"
},
{
title: "页面",
url: "3",
name: "pages"
},
{
title: "其他",
url: "3",
name: "other"
}
];

View File

@@ -0,0 +1,18 @@
<template>
<div style="display: inline-block;">
<Icon type="ios-loading" size="18" color="#2d8cf0" class="spin-icon-load"></Icon>
</div>
</template>
<script>
export default {
name: "circleLoading"
};
</script>
<style lang="scss" scoped>
.spin-icon-load {
animation: ani-demo-spin 1s linear infinite;
}
</style>

View File

@@ -0,0 +1,72 @@
<template>
<div>
<Cascader
v-model="selectDep"
:data="department"
@on-change="handleChangeDep"
change-on-select
filterable
clearable
placeholder="请选择"
></Cascader>
</div>
</template>
<script>
import { initDepartment } from "@/api/index";
export default {
name: "departmentChoose",
props: {
},
data() {
return {
selectDep: [], // 已选数据
department: [] // 列表
};
},
methods: {
// 获取部门数据
initDepartmentData() {
initDepartment().then(res => {
if (res.success) {
const arr = res.result;
this.filterData(arr)
this.department = arr
}
});
},
handleChangeDep(value, selectedData) {
let departmentId = "";
// 获取最后一个值
if (value && value.length > 0) {
departmentId = value[value.length - 1];
}
this.$emit("on-change", departmentId);
},
// 清空已选列表
clearSelect() {
this.selectDep = [];
},
// 处理部门数据
filterData (data) {
data.forEach(e => {
e.value = e.id;
e.label = e.title;
if (e.children) {
this.filterData(e.children)
} else {
return
}
})
}
},
created() {
this.initDepartmentData();
}
};
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,159 @@
<template>
<div>
<div style="display:flex;">
<Input
v-model="departmentTitle"
readonly
style="margin-right:10px;"
:placeholder="placeholder"
:clearable="clearable"
@on-clear="clearSelect"
/>
<Poptip transfer trigger="click" placement="right" title="选择部门" width="250">
<Button icon="md-list">选择部门</Button>
<div slot="content">
<Input
v-model="searchKey"
suffix="ios-search"
@on-change="searchDep"
placeholder="输入部门名搜索"
clearable
/>
<div class="dep-tree-bar">
<Tree
:data="dataDep"
@on-select-change="selectTree"
></Tree>
<Spin size="large" fix v-if="depLoading"></Spin>
</div>
</div>
</Poptip>
</div>
</div>
</template>
<script>
import {initDepartment, searchDepartment} from "@/api/index";
export default {
name: "departmentTreeChoose",
props: {
multiple: {
type: Boolean,
default: false
},
clearable: {
type: Boolean,
default: true
},
placeholder: {
type: String,
default: "点击选择部门"
}
},
data() {
return {
depLoading: false, // 加载状态
departmentTitle: "", // modal标题
searchKey: "", // 搜索关键词
dataDep: [], // 部门列表
selectDep: [], // 已选部门
departmentId: [] // 部门id
};
},
methods: {
// 获取部门数据
initDepartmentData() {
initDepartment().then(res => {
if (res.success) {
this.dataDep = res.result;
}
});
},
searchDep() {
// 搜索部门
if (this.searchKey) {
this.depLoading = true;
searchDepartment({title: this.searchKey}).then(res => {
this.depLoading = false;
if (res.success) {
res.result.forEach(function (e) {
if (e.status == -1) {
e.title = "[已禁用] " + e.title;
e.disabled = true;
}
});
this.dataDep = res.result;
}
});
} else {
this.initDepartmentData();
}
},
// 选择回调
selectTree(v) {
if (v.length === 0) {
this.$emit("on-change", null);
this.departmentId = "";
this.departmentTitle = "";
return
}
this.departmentId = v[0].id;
this.departmentTitle = v[0].title;
let department = {
departmentId: this.departmentId,
departmentTitle: this.departmentTitle
}
this.$emit("on-change", department);
},
// 清除选中方法
clearSelect() {
this.departmentId = [];
this.departmentTitle = "";
this.initDepartmentData();
if (this.multiple) {
this.$emit("on-change", []);
} else {
this.$emit("on-change", "");
}
this.$emit("on-clear");
},
// 设置数据 回显用
setData(ids, title) {
this.departmentTitle = title;
if (this.multiple) {
this.departmentId = ids;
} else {
this.departmentId = [];
this.departmentId.push(ids);
}
}
},
created() {
this.initDepartmentData();
}
};
</script>
<style lang="scss" scoped>
.dep-tree-bar {
position: relative;
min-height: 80px;
max-height: 500px;
overflow: auto;
margin-top: 5px;
}
.dep-tree-bar::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.dep-tree-bar::-webkit-scrollbar-thumb {
border-radius: 4px;
-webkit-box-shadow: inset 0 0 2px #d1d1d1;
background: #e4e4e4;
}
</style>

View File

@@ -0,0 +1,168 @@
<template>
<div class="set-password">
<Poptip transfer trigger="focus" placement="right" width="250">
<Input
type="password"
password
style="width:350px;"
:maxlength="maxlength"
v-model="currentValue"
@on-change="handleChange"
:size="size"
:placeholder="placeholder"
:disabled="disabled"
:readonly="readonly"
/>
<div :class="tipStyle" slot="content">
<div class="words">强度 : {{strength}}</div>
<Progress
:percent="strengthValue"
:status="progressStatus"
hide-info
style="margin: 13px 0;"
/>
<br />请至少输入 6 个字符请不要使
<br />用容易被猜到的密码
</div>
</Poptip>
</div>
</template>
<script>
export default {
name: "setPassword",
props: {
value: String,
size: String,
placeholder: {
type: String,
default: "请输入密码长度为6-20个字符"
},
disabled: {
type: Boolean,
default: false
},
readonly: {
type: Boolean,
default: false
},
maxlength: {
type: Number,
default: 20
}
},
data() {
return {
currentValue: this.value, // 当前值
tipStyle: "password-tip-none", // 强度样式
strengthValue: 0, // 强度等级
progressStatus: "normal", // 进度条状态
strength: "无", // 密码强度描述
grade: 0 // 强度等级
};
},
methods: {
checkStrengthValue(v) {
// 评级制判断密码强度 最高5
let grade = 0;
if (/\d/.test(v)) {
grade++; //数字
}
if (/[a-z]/.test(v)) {
grade++; //小写
}
if (/[A-Z]/.test(v)) {
grade++; //大写
}
if (/\W/.test(v)) {
grade++; //特殊字符
}
if (v.length >= 10) {
grade++;
}
this.grade = grade;
return grade;
},
strengthChange() {
if (!this.currentValue) {
this.tipStyle = "password-tip-none";
this.strength = "无";
this.strengthValue = 0;
return;
}
let grade = this.checkStrengthValue(this.currentValue);
if (grade <= 1) {
this.progressStatus = "wrong";
this.tipStyle = "password-tip-weak";
this.strength = "弱";
this.strengthValue = 33;
} else if (grade >= 2 && grade <= 4) {
this.progressStatus = "normal";
this.tipStyle = "password-tip-middle";
this.strength = "中";
this.strengthValue = 66;
} else {
this.progressStatus = "success";
this.tipStyle = "password-tip-strong";
this.strength = "强";
this.strengthValue = 100;
}
},
// 密码变动后回调
handleChange(v) {
this.strengthChange();
this.$emit("input", this.currentValue);
this.$emit("on-change", this.currentValue, this.grade, this.strength);
},
// 监听密码变动,实时回显密码强度
setCurrentValue(value) {
if (value === this.currentValue) {
return;
}
this.currentValue = value;
this.strengthChange();
this.$emit("on-change", this.currentValue, this.grade, this.strength);
}
},
watch: {
value(val) {
this.setCurrentValue(val);
}
}
};
</script>
<style lang="scss" scoped>
.set-password .ivu-poptip,
.set-password .ivu-poptip-rel {
display: block;
}
.password-tip-none {
padding: 1vh 0;
}
.password-tip-weak {
padding: 1vh 0;
.words {
color: #ed3f14;
}
}
.password-tip-middle {
padding: 1vh 0;
.words {
color: #2d8cf0;
}
}
.password-tip-strong {
padding: 1vh 0;
.words {
color: #52c41a;
}
}
</style>

View File

@@ -0,0 +1,176 @@
<template>
<div>
<div style="display:flex;">
<Input
v-model="currentValue"
@on-change="handleChange"
v-show="showInput"
:placeholder="placeholder"
:size="size"
:disabled="disabled"
:readonly="readonly"
:maxlength="maxlength"
>
<Poptip slot="append" transfer trigger="hover" title="图片预览" placement="right">
<Icon type="md-eye" class="see-icon" />
<div slot="content">
<img :src="currentValue" alt="该资源不存在" style="width: 100%;margin: 0 auto;display: block;" />
<a @click="viewImage=true" style="margin-top:5px;text-align:right;display:block">查看大图</a>
</div>
</Poptip>
</Input>
<Upload
:action="uploadFileUrl"
:headers="accessToken"
:on-success="handleSuccess"
:on-error="handleError"
:format="['jpg','jpeg','png','gif','bmp']"
accept=".jpg, .jpeg, .png, .gif, .bmp"
:max-size="1024"
:on-format-error="handleFormatError"
:on-exceeded-size="handleMaxSize"
:before-upload="beforeUpload"
:show-upload-list="false"
ref="up"
class="upload"
>
<Button :loading="loading" :size="size" :disabled="disabled">上传图片</Button>
</Upload>
</div>
<Modal title="图片预览" v-model="viewImage" :styles="{top: '30px'}" draggable>
<img :src="currentValue" alt="该资源不存在" style="width: 100%;margin: 0 auto;display: block;" />
<div slot="footer">
<Button @click="viewImage=false">关闭</Button>
</div>
</Modal>
</div>
</template>
<script>
import { uploadFile } from "@/api/index";
export default {
name: "uploadPicInput",
props: {
value: String,
size: {
default: 'default',
type: String
},
placeholder: {
type: String,
default: "图片链接"
},
showInput: {
type: Boolean,
default: true
},
disabled: {
type: Boolean,
default: false
},
readonly: {
type: Boolean,
default: false
},
maxlength: Number,
icon: {
type: String,
default: "ios-cloud-upload-outline"
}
},
data() {
return {
accessToken: {}, // 验证token
currentValue: this.value, // 当前值
loading: false, // 加载状态
viewImage: false, // 预览图片modal
uploadFileUrl: uploadFile // 上传地址
};
},
methods: {
// 初始化
init() {
this.accessToken = {
accessToken: this.getStore("accessToken")
};
},
// 格式校验
handleFormatError(file) {
this.loading = false;
this.$Notice.warning({
title: "不支持的文件格式",
desc:
"所选文件‘ " +
file.name +
" ’格式不正确, 请选择 .jpg .jpeg .png .gif .bmp格式文件"
});
},
// 大小校验
handleMaxSize(file) {
this.loading = false;
this.$Notice.warning({
title: "文件大小过大",
desc: "所选文件大小过大, 不得超过1M."
});
},
// 上传前
beforeUpload() {
this.loading = true;
return true;
},
// 上传成功
handleSuccess(res, file) {
this.loading = false;
if (res.success) {
this.currentValue = res.result;
this.$emit("input", this.currentValue);
this.$emit("on-change", this.currentValue);
} else {
// this.$Message.error(res.message);
}
},
// 上传失败
handleError(error, file, fileList) {
this.loading = false;
this.$Message.error(error.toString());
},
// 上传成功回显
handleChange(v) {
this.$emit("input", this.currentValue);
this.$emit("on-change", this.currentValue);
this.$attrs.rollback && this.$attrs.rollback()
},
// 初始值
setCurrentValue(value) {
if (value === this.currentValue) {
return;
}
this.currentValue = value;
this.$emit("on-change", this.currentValue);
}
},
watch: {
value(val) {
this.setCurrentValue(val);
}
},
created() {
this.init();
}
};
</script>
<style lang="scss" scoped>
.see-icon {
font-size: 16px;
cursor: pointer;
}
.upload {
display: inline-block;
margin-left: 10px;
}
</style>

View File

@@ -0,0 +1,294 @@
<template>
<div>
<div class="upload-pic-thumb">
<vuedraggable
:list="uploadList"
:disabled="!draggable||!multiple"
:animation="200"
class="list-group"
ghost-class="thumb-ghost"
@end="onEnd"
>
<div class="upload-list" v-for="(item, index) in uploadList" :key="index">
<div v-if="item.status == 'finished'" style="height:60px;">
<img :src="item.url" />
<div class="upload-list-cover">
<Icon type="ios-eye-outline" @click="handleView(item.url)"></Icon>
<Icon type="ios-trash-outline" @click="handleRemove(item)"></Icon>
</div>
</div>
<div v-else>
<Progress v-if="item.showProgress" :percent="item.percentage" hide-info></Progress>
</div>
</div>
</vuedraggable>
<Upload
ref="upload"
:multiple="multiple"
:show-upload-list="false"
:on-success="handleSuccess"
:on-error="handleError"
:format="['jpg','jpeg','png','gif']"
:max-size="1024"
:on-format-error="handleFormatError"
:on-exceeded-size="handleMaxSize"
:before-upload="handleBeforeUpload"
type="drag"
:action="uploadFileUrl"
:headers="accessToken"
style="display: inline-block;width:58px;"
>
<div style="width: 58px;height:58px;line-height: 58px;">
<Icon type="md-camera" size="20"></Icon>
</div>
</Upload>
</div>
<Modal title="图片预览" v-model="viewImage" :styles="{top: '30px'}" draggable>
<img :src="imgUrl" alt="无效的图片链接" style="width: 100%;margin: 0 auto;display: block;" />
<div slot="footer">
<Button @click="viewImage=false">关闭</Button>
</div>
</Modal>
</div>
</template>
<script>
import { uploadFile } from "@/api/index";
import vuedraggable from "vuedraggable";
export default {
name: "uploadPicThumb",
components: {
vuedraggable
},
props: {
value: {
type: null
},
draggable: {
type: Boolean,
default: true
},
multiple: {
type: Boolean,
default: true
},
limit: {
type: Number,
default: 10
}
},
data() {
return {
accessToken: {}, // 验证token
uploadFileUrl: uploadFile, // 上传地址
uploadList: [], // 上传列表
viewImage: false, // 预览modal
imgUrl: "" // 图片地址
};
},
methods: {
// 拖拽结束事件
onEnd() {
this.returnValue();
},
// 初始化方法
init() {
this.setData(this.value, true);
this.accessToken = {
accessToken: this.getStore("accessToken")
};
},
// 预览图片
handleView(imgUrl) {
this.imgUrl = imgUrl;
this.viewImage = true;
},
// 移除图片
handleRemove(file) {
this.uploadList = this.uploadList.filter(i => i.url !== file.url);
this.returnValue();
},
// 上传成功
handleSuccess(res, file) {
if (res.success) {
file.url = res.result;
// 单张图片处理
if (!this.multiple && this.uploadList.length > 0) {
// 删除第一张
this.uploadList.splice(0, 1);
}
this.uploadList.push(file);
// 返回组件值
this.returnValue();
} else {
this.$Message.error(res.message);
}
},
// 上传失败
handleError(error, file, fileList) {
this.$Message.error(error.toString());
},
// 格式校验
handleFormatError(file) {
this.$Notice.warning({
title: "不支持的文件格式",
desc:
"所选文件‘ " +
file.name +
" ’格式不正确, 请选择 .jpg .jpeg .png .gif图片格式文件"
});
},
// 上传文件大小校验
handleMaxSize(file) {
this.$Notice.warning({
title: "文件大小过大",
desc:
"所选文件大小过大不能超过1M."
});
},
// 上传之前钩子
handleBeforeUpload() {
if (this.multiple && this.uploadList.length >= this.limit) {
this.$Message.warning("最多只能上传" + this.limit + "张图片");
return false;
}
return true;
},
// 返回组件值
returnValue() {
if (!this.uploadList || this.uploadList.length < 1) {
if (!this.multiple) {
this.$emit("input", "");
this.$emit("on-change", "");
} else {
this.$emit("input", []);
this.$emit("on-change", []);
}
return;
}
if (!this.multiple) {
// 单张
let v = this.uploadList[0].url;
this.$emit("input", v);
this.$emit("on-change", v);
} else {
let v = [];
this.uploadList.forEach(e => {
v.push(e.url);
});
this.$emit("input", v);
this.$emit("on-change", v);
}
},
// 传入值变化时改变值
setData(v, init) {
if (typeof v == "string") {
// 单张
if (this.multiple) {
this.$Message.warning("多张上传仅支持数组数据类型");
return;
}
if (!v) {
return;
}
this.uploadList = [];
let item = {
url: v,
status: "finished"
};
this.uploadList.push(item);
this.$emit("uploadchange", v);
this.$emit("on-change", v);
} else if (typeof v == "object") {
// 多张
if (!this.multiple) {
this.$Message.warning("单张上传仅支持字符串数据类型");
return;
}
this.uploadList = [];
if (v.length > this.limit) {
for (let i = 0; i < this.limit; i++) {
let item = {
url: v[i],
status: "finished"
};
this.uploadList.push(item);
}
this.$emit("on-change", v.slice(0, this.limit));
if (init) {
this.$emit("input", v.slice(0, this.limit));
}
this.$Message.warning("最多只能上传" + this.limit + "张图片");
} else {
v.forEach(e => {
let item = {
status: "finished",
...e
};
this.uploadList.push(item);
});
this.$emit("on-change", v);
}
}
}
},
watch: {
value(val) {
this.setData(val);
}
},
mounted() {
this.init();
}
};
</script>
<style lang="scss" scoped>
.upload-pic-thumb{
display: flex;
}
.upload-list {
display: inline-block;
width: 60px;
height: 60px;
text-align: center;
line-height: 60px;
border: 1px solid transparent;
border-radius: 4px;
overflow: hidden;
background: #fff;
position: relative;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
margin-right: 5px;
vertical-align: middle;
}
.upload-list img {
width: 100%;
height: -webkit-fill-available;
}
.upload-list-cover {
display: none;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.6);
}
.upload-list:hover .upload-list-cover {
display: block;
}
.upload-list-cover i {
color: #fff;
font-size: 20px;
cursor: pointer;
margin: 0 2px;
}
.list-group {
display: inline-block;
}
.thumb-ghost {
opacity: 0.5;
background: #c8ebfb;
}
</style>

View File

@@ -0,0 +1,186 @@
<template>
<div class="map">
<Modal v-model="showMap" title="选择地址" width="800">
<div class="address">{{ addrContent.address }}</div>
<div id="map-container"></div>
<div class="search-con">
<Input
placeholder="输入关键字搜索"
id="input-map"
v-model="mapSearch"
/>
<ul>
<li
v-for="(tip, index) in tips"
:key="index"
@click="selectAddr(tip.location)"
>
<p>{{ tip.name }}</p>
<p>{{ tip.district + tip.address }}</p>
</li>
</ul>
</div>
<div slot="footer">
<Button type="default" @click="showMap = false">取消</Button>
<Button type="primary" :loading="loading" @click="ok">确定</Button>
</div>
</Modal>
</div>
</template>
<script>
import AMapLoader from "@amap/amap-jsapi-loader";
import { getRegion } from "@/api/common.js";
const config = require('@/config/index')
export default {
data() {
return {
config,
showMap: false, // modal显隐
mapSearch: "", // 地图搜索
map: null, // 初始化地图
autoComplete: null, // 初始化搜索方法
geocoder: null, // 初始化地理、坐标转化
positionPicker: null, // 地图拖拽选点
tips: [], //搜索关键字列表
addrContent: {}, // 回显地址信息
loading: false, // 加载状态
};
},
watch: {
mapSearch: function (val) {
this.searchOfMap(val);
},
},
methods: {
ok() {
// 确定选择
this.loading = true;
const params = {
cityCode: this.addrContent.regeocode.addressComponent.citycode,
townName: this.addrContent.regeocode.addressComponent.township,
};
getRegion(params).then((res) => {
if (res.success) {
this.addrContent.addr = res.result.name.replace(/,/g, " ");
this.addrContent.addrId = res.result.id;
this.loading = false;
this.showMap = false;
this.$emit("getAddress", this.addrContent);
}
});
},
// 初始化地图组件
init() {
AMapLoader.load({
key: this.config.aMapKey, // 申请好的Web端开发者Key首次调用 load 时必填
version: "", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: [
"AMap.ToolBar",
"AMap.Autocomplete",
"AMap.PlaceSearch",
"AMap.Geolocation",
"AMap.Geocoder",
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
AMapUI: {
// 是否加载 AMapUI缺省不加载
version: "1.1", // AMapUI 缺省 1.1
plugins: ["misc/PositionPicker"], // 需要加载的 AMapUI ui插件
},
}).then((AMap) => {
let that = this;
this.map = new AMap.Map("map-container", {
zoom: 12,
});
that.map.addControl(new AMap.ToolBar());
that.map.addControl(new AMap.Autocomplete());
that.map.addControl(new AMap.PlaceSearch());
that.map.addControl(new AMap.Geocoder());
// 实例化Autocomplete
let autoOptions = {
city: "全国",
};
that.autoComplete = new AMap.Autocomplete(autoOptions); // 搜索
that.geocoder = new AMap.Geocoder(autoOptions);
that.positionPicker = new AMapUI.PositionPicker({
// 拖拽选点
mode: "dragMap",
map: that.map,
});
that.positionPicker.start();
/**
*
* 所有回显数据都在positionResult里面
* 需要字段可以查找
*
*/
that.positionPicker.on("success", function (positionResult) {
that.addrContent = positionResult;
});
}).catch((e) => {});
},
searchOfMap(val) {
// 地图搜索
let that = this;
this.autoComplete.search(val, function (status, result) {
// 搜索成功时result即是对应的匹配数据
if (status == "complete" && result.info == "OK") {
that.tips = result.tips;
} else {
that.tips = [];
}
});
},
selectAddr(location) {
// 选择坐标
if (!location) {
this.$Message.warning("请选择正确点位");
return false;
}
const lnglat = [location.lng, location.lat];
this.positionPicker.start(lnglat);
},
},
mounted() {
this.init();
},
};
</script>
<style lang="scss" scoped>
#map-container {
width: 500px;
height: 400px;
}
.search-con {
position: absolute;
right: 20px;
top: 64px;
width: 260px;
ul {
width: 260px;
height: 400px;
overflow: scroll;
li {
padding: 5px;
p:nth-child(2) {
color: #999;
font-size: 12px;
}
&:hover {
background-color: #eee;
cursor: pointer;
}
}
}
}
.address {
margin-bottom: 10px;
// color: $theme_color;
font-weight: bold;
}
</style>

View File

@@ -0,0 +1,182 @@
<template>
<div>
<Cascader
:data="data"
:load-data="loadData"
v-model="addr"
placeholder="请选择地址"
@on-change="change"
style="width: 350px"
></Cascader>
</div>
</template>
<script>
import {getChildRegion} from '@/api/common.js';
export default {
data () {
return {
data: [], // 地区数据
addr: [] // 已选数据
};
},
props: ['addressId'],
mounted () {},
methods: {
change (val, selectedData) { // 选择地区
/**
* @returns [regionId,region]
*/
this.$emit('selected', [
val,
selectedData[selectedData.length - 1].__label.split('/')
]);
},
loadData (item, callback) { // 加载数据
item.loading = true;
getChildRegion(item.value).then((res) => {
if (res.result.length <= 0) {
item.loading = false;
} else {
res.result.forEach((child) => {
item.loading = false;
let data = {
value: child.id,
label: child.name,
loading: false,
children: []
};
if (child.level === 'street' || item.label === '香港特别行政区') {
item.children.push({
value: child.id,
label: child.name
});
} else {
item.children.push(data);
}
});
callback();
}
});
},
async init () { // 初始化地图数据
let data = await getChildRegion(0);
let arr = [];
data.result.forEach((item) => {
let obj;
// 台湾省做处理
if (item.name === '台湾省') {
obj = {
value: item.id,
label: item.name
};
} else {
obj = {
value: item.id,
label: item.name,
loading: false,
children: []
};
}
arr.push(obj);
});
this.data = arr;
},
async reviewData () {
// 数据回显
let addr = JSON.parse(JSON.stringify(this.addressId.split(',')));
let length = addr.length;
let data = await getChildRegion(0);
let arr0 = [];
let arr1 = [];
let arr2 = [];
// 第一级数据
data.result.forEach((item) => {
let obj;
// 台湾省做处理
if (item.name === '台湾省') {
obj = {
value: item.id,
label: item.name
};
} else {
obj = {
value: item.id,
label: item.name,
loading: false,
children: []
};
}
arr0.push(obj);
});
// 根据选择的数据来加载数据列表
if (length > 0) {
let children = await getChildRegion(addr[0]);
children = this.handleData(children.result);
arr0.forEach((e) => {
if (e.value === addr[0]) {
e.children = arr1 = children;
}
});
}
if (length > 1) {
let children = await getChildRegion(addr[1]);
children = this.handleData(children.result);
arr1.forEach((e) => {
if (e.value === addr[1]) {
e.children = arr2 = children;
}
});
}
if (length > 2) {
let children = await getChildRegion(addr[2]);
children = this.handleData(children.result);
arr2.forEach((e) => {
if (e.value === addr[2]) {
e.children = children;
}
});
}
this.data = arr0;
this.addr = addr;
},
handleData (data) {
// 处理接口数据
let item = [];
data.forEach((child) => {
let obj = {
value: child.id,
label: child.name,
loading: false,
children: []
};
if (child.level === 'street' || item.label === '香港特别行政区') {
item.push({
value: child.id,
label: child.name
});
} else {
item.push(obj);
}
});
return item;
}
},
watch: {
addressId: {
handler: function (v) {
if (v) {
this.reviewData();
} else {
this.init();
}
},
immediate: true
}
}
};
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,117 @@
// text
@prefixCls: zk-checkbox;
// color
@border: #dddee1;
@hoverBorder: #bcbcbc;
@blue: #2d8cf0;
.@{prefixCls}-wrapper {
display: flex;
justify-content: center;
}
.@{prefixCls} {
display: inline-block;
position: relative;
line-height: 1;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
outline: none;
&:hover {
.@{prefixCls}__inner {
border-color: @hoverBorder;
}
}
}
.@{prefixCls}__inner {
display: inline-block;
width: 14px;
height: 14px;
position: relative;
top: 0;
left: 0;
border: 1px solid @border;
border-radius: 2px;
background-color: #ffffff;
transition: border-color .2s ease-in-out,background-color .2s ease-in-out;
&::after {
content: "";
display: table;
width: 4px;
height: 8px;
position: absolute;
top: 1px;
left: 4px;
border: 2px solid #fff;
border-top: 0;
border-left: 0;
transform: rotate(45deg) scale(0);
transition: all .2s ease-in-out;
}
}
.@{prefixCls}--indeterminate {
.@{prefixCls}__inner {
background-color: @blue;
border-color: @blue;
&::after {
content: "";
width: 8px;
height: 1px;
transform: scale(1);
position: absolute;
left: 2px;
top: 5px;
}
}
&:hover {
.@{prefixCls}__inner {
border-color: @blue;
}
}
}
.@{prefixCls}--checked {
.@{prefixCls}__inner {
border-color: @blue;
background-color: @blue;
&::after {
content: "";
display: table;
width: 4px;
height: 8px;
position: absolute;
top: 1px;
left: 4px;
border: 2px solid #ffffff;
border-top: 0;
border-left: 0;
transform: rotate(45deg) scale(1);
transition: all .2s ease-in-out;
}
}
&:hover {
.@{prefixCls}__inner {
border-color: @blue;
}
}
}
.@{prefixCls}--disabled {
cursor: not-allowed;
.@{prefixCls}__inner {
background-color: #f3f3f3;
border-color: @border;
&::after {
animation-name: none;
border-color: #ccc;
}
}
&:hover {
.@{prefixCls}__inner {
border-color: @border;
}
}
}

View File

@@ -0,0 +1,58 @@
<template lang="html">
<label :class="`${prefixCls}-wrapper`">
<span :class="checkboxClass">
<span
:class="`${prefixCls}__inner`"
@click="toggle"></span>
</span>
</label>
</template>
<script>
export default {
name: 'zk-checkbox',
props: {
value: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
indeterminate: {
type: Boolean,
default: false,
},
},
data() {
return {
prefixCls: 'zk-checkbox',
};
},
computed: {
checkboxClass() {
return [
`${this.prefixCls}`,
{
[`${this.prefixCls}--disabled`]: this.disabled,
[`${this.prefixCls}--checked`]: this.value,
[`${this.prefixCls}--indeterminate`]: this.indeterminate,
},
];
},
},
methods: {
toggle() {
if (this.disabled) {
return false;
}
const value = !this.value;
this.$emit('input', value);
return this.$emit('on-change', value);
},
},
};
</script>
<style lang="less" scoped src="./Checkbox.less"></style>

View File

@@ -0,0 +1,170 @@
@import "./font/iconfont";
// text
@prefixCls: zk-table;
// color
@black: #515a6e;
@white: #ffffff;
@border: #e9eaec;
@hoverRow: #ebf7ff;
@backgroundRow: #f8f8f9;
.@{prefixCls} {
position: relative;
width: 100%;
box-sizing: border-box;
background-color: @white;
border: 1px solid @border;
font-size: 14px;
line-height: 26px;
color: @black;
overflow: hidden;
}
.@{prefixCls}__header-cell {
font-weight: bold;
}
.@{prefixCls}__cell-inner {
padding: 0px 18px;
}
.@{prefixCls}-default {
font-size: 14px;
.@{prefixCls}__header-row {
height: 40px !important;
}
.@{prefixCls}__body-row {
height: 48px !important;
}
}
.@{prefixCls}-small {
font-size: 12px;
.@{prefixCls}__header-row {
height: 36px !important;
}
.@{prefixCls}__body-row {
height: 40px !important;
}
}
.@{prefixCls}-large {
font-size: 16px;
.@{prefixCls}__header-row {
height: 43px !important;
}
.@{prefixCls}__body-row {
height: 60px !important;
}
}
.@{prefixCls}__header-wrapper,
.@{prefixCls}__footer-wrapper {
overflow: hidden;
}
.@{prefixCls}__body-wrapper {
overflow: auto;
}
.@{prefixCls}__header,
.@{prefixCls}__body,
.@{prefixCls}__footer {
width: 100%;
table-layout: fixed;
border-collapse: collapse;
border-spacing: 0;
}
.@{prefixCls}__header-row {
height: 40px;
box-sizing: border-box;
background-color: @backgroundRow;
border-bottom: 1px solid @border;
}
.@{prefixCls}__footer-row {
height: 40px;
box-sizing: border-box;
background-color: @white;
border-top: 1px solid @border;
}
.@{prefixCls}__body-row {
height: 48px;
box-sizing: border-box;
&:not(:first-of-type) {
border-top: 1px solid @border;
}
}
.@{prefixCls}__header-cell,
.@{prefixCls}__body-cell,
.@{prefixCls}__footer-cell {
box-sizing: border-box;
text-align: left;
vertical-align: middle;
word-break: break-all;
overflow: hidden;
}
.@{prefixCls}--firstProp-header-inner {
padding-left: 32px;
}
.@{prefixCls}--empty-row {
height: 80px;
}
.@{prefixCls}--empty-content {
text-align: center;
}
.@{prefixCls}--center-cell {
text-align: center;
}
.@{prefixCls}--right-cell {
text-align: right;
}
.@{prefixCls}--stripe-row {
background-color: @backgroundRow;
}
.@{prefixCls}--row-hover {
background-color: @hoverRow;
}
.@{prefixCls}--border-cell {
&:not(:last-of-type) {
border-right: 1px solid @border;
}
}
.@{prefixCls}--tree-icon {
margin-right: 6px;
cursor: pointer;
}
.@{prefixCls}--expand-inner {
text-align: center;
cursor: pointer;
transition: transform .2s ease-in-out;
}
.@{prefixCls}--expanded-inner {
transform: rotate(90deg);
}
.@{prefixCls}--expand-content {
padding: 20px;
}

View File

@@ -0,0 +1,396 @@
<template lang="html">
<div
v-if="columns.length > 0"
ref="table"
:class="[prefixCls, `${prefixCls}-${size}`, tableClass]">
<Spin fix v-if="loading"></Spin>
<div
v-show="showHeader"
ref="header-wrapper"
:class="`${prefixCls}__header-wrapper`"
@mousewheel="handleEvent('header', $event)">
<table-header
ref="table-header">
</table-header>
</div>
<div
ref="body-wrapper"
:style="bodyWrapperStyle"
:class="`${prefixCls}__body-wrapper`"
@scroll="handleEvent('body', $event)">
<table-body
ref="table-body"
:class="bodyClass">
</table-body>
</div>
<div
v-show="showSummary && data.length > 0"
ref="footer-wrapper"
:class="`${prefixCls}__footer-wrapper`"
@mousewheel="handleEvent('footer', $event)">
<table-footer
ref="table-footer">
</table-footer>
</div>
</div>
</template>
<script>
import TableHeader from "./TableHeader";
import TableBody from "./TableBody";
import TableFooter from "./TableFooter";
import { mixins, scrollBarWidth as getSbw } from "./utils";
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-param-reassign */
// function getBodyData(data, isTreeType, childrenProp, isFold, level = 1) {
function getBodyData(
primaryKey,
oldBodyData,
data,
isTreeType,
childrenProp,
isFold,
parentFold,
level = 1
) {
let bodyData = [];
data.forEach((row, index) => {
const children = row[childrenProp];
const childrenLen =
Object.prototype.toString.call(children).slice(8, -1) === "Array"
? children.length
: 0;
let curIsFold = isFold;
if (
isFold &&
typeof primaryKey === "string" &&
Array.isArray(oldBodyData)
) {
for (let i = 0; i < oldBodyData.length; i++) {
const oldRow = oldBodyData[i];
if (oldRow[primaryKey] === row[primaryKey]) {
if ("_isFold" in oldRow) {
curIsFold = oldRow._isFold;
}
break;
}
}
}
bodyData.push({
_isHover: false,
_isExpanded: false,
_isChecked: false,
_level: level,
// _isHide: isFold ? level !== 1 : false,
// _isFold: isFold,
_isHide: level !== 1 ? isFold && parentFold : false,
_isFold: isFold && curIsFold,
_childrenLen: childrenLen,
_normalIndex: index + 1,
...row
});
if (isTreeType) {
if (childrenLen > 0) {
// bodyData = bodyData.concat(getBodyData(children, true, childrenProp, isFold, level + 1));
bodyData = bodyData.concat(
getBodyData(
primaryKey,
oldBodyData,
children,
true,
childrenProp,
isFold,
curIsFold,
level + 1
)
);
}
}
});
return bodyData;
}
function initialState(table, expandKey) {
return {
bodyHeight: "auto",
firstProp: expandKey || (table.columns[0] && table.columns[0].key),
// bodyData: getBodyData(table.data, table.treeType, table.childrenProp, table.isFold),
bodyData: getBodyData(
table.primaryKey,
table.bodyData,
table.data,
table.treeType,
table.childrenProp,
table.isFold,
false
)
};
}
function initialColumns(table, clientWidth) {
let columnsWidth = 0;
const minWidthColumns = [];
const otherColumns = [];
const columns = table.columns.concat();
if (table.expandType) {
columns.unshift({
width: "50"
});
}
if (table.selectable) {
columns.unshift({
width: "50"
});
}
if (table.showIndex) {
columns.unshift({
width: "50px",
key: "_normalIndex",
title: table.indexText
});
}
columns.forEach((column, index) => {
let width = "";
let minWidth = "";
if (!column.width) {
if (column.minWidth) {
minWidth =
typeof column.minWidth === "number"
? column.minWidth
: parseInt(column.minWidth, 10);
} else {
minWidth = 80;
}
minWidthColumns.push({
...column,
minWidth,
_index: index
});
} else {
width =
typeof column.width === "number"
? column.width
: parseInt(column.width, 10);
otherColumns.push({
...column,
width,
_index: index
});
}
columnsWidth += minWidth || width;
});
const scrollBarWidth = getSbw();
const totalWidth = columnsWidth + scrollBarWidth;
const isScrollX = totalWidth > clientWidth;
if (!isScrollX) {
const extraWidth = clientWidth - totalWidth;
const averageExtraWidth = Math.floor(extraWidth / minWidthColumns.length);
minWidthColumns.forEach(column => {
column.computedWidth = column.minWidth + averageExtraWidth;
});
}
const tableColumns = otherColumns.concat(minWidthColumns);
tableColumns.sort((a, b) => a._index - b._index);
return tableColumns;
}
export default {
name: "TreeTable",
mixins: [mixins],
components: {
TableHeader,
TableBody,
TableFooter
},
props: {
data: {
type: Array,
default: () => []
},
columns: {
type: Array,
default: () => []
},
size: {
default() {
return !this.$IVIEW || this.$IVIEW.size === ""
? "default"
: this.$IVIEW.size;
}
},
loading: {
type: Boolean,
default: false
},
maxHeight: {
type: [String, Number],
default: "auto"
},
stripe: {
type: Boolean,
default: false
},
border: {
type: Boolean,
default: false
},
treeType: {
type: Boolean,
default: true
},
childrenProp: {
type: String,
default: "children"
},
isFold: {
type: Boolean,
default: true
},
expandType: {
type: Boolean,
default: true
},
selectable: {
type: Boolean,
default: true
},
selectType: {
type: String,
default: "checkbox"
},
emptyText: {
type: String,
default: "暂无数据"
},
showHeader: {
type: Boolean,
default: true
},
showIndex: {
type: Boolean,
default: false
},
indexText: {
type: String,
default: "#"
},
showSummary: {
type: Boolean,
default: false
},
sumText: {
type: String,
default: "合计"
},
primaryKey: String,
summaryMethod: Function,
showRowHover: {
type: Boolean,
default: true
},
rowKey: Function,
rowClassName: [String, Function],
cellClassName: [String, Function],
rowStyle: [Object, Function],
cellStyle: [Object, Function],
expandKey: String
},
data() {
return {
computedWidth: "",
computedHeight: "",
tableColumns: [],
...initialState(this, this.expandKey)
};
},
computed: {
bodyWrapperStyle() {
return {
height: this.bodyHeight
};
},
tableClass() {
return {
[`${this.prefixCls}--border`]: this.border
};
},
bodyClass() {
return {
[`${this.prefixCls}--stripe`]: this.stripe
};
}
},
methods: {
handleEvent(type, $event) {
this.validateType(type, ["header", "body", "footer"], "handleEvent");
const eventType = $event.type;
if (eventType === "scroll") {
this.$refs["header-wrapper"].scrollLeft = $event.target.scrollLeft;
this.$refs["footer-wrapper"].scrollLeft = $event.target.scrollLeft;
}
if (eventType === "mousewheel") {
const deltaX = $event.deltaX;
const $body = this.$refs["body-wrapper"];
if (deltaX > 0) {
$body.scrollLeft += 10;
} else {
$body.scrollLeft -= 10;
}
}
return this.$emit(`${type}-${eventType}`, $event);
},
// computedWidth, computedHeight, tableColumns
measure() {
this.$nextTick(() => {
const { clientWidth, clientHeight } = this.$el;
this.computedWidth = clientWidth + 2;
this.computedHeight = clientHeight + 2;
const maxHeight = parseInt(this.maxHeight, 10);
if (this.maxHeight !== "auto" && this.computedHeight > maxHeight) {
this.bodyHeight = `${maxHeight - 83}px`;
}
this.tableColumns = initialColumns(this, clientWidth);
});
},
getCheckedProp(key = "index") {
if (!this.selectable) {
return [];
}
const checkedIndexs = [];
this.bodyData.forEach((item, index) => {
if (item._isChecked) {
if (key === "index") {
checkedIndexs.push(index);
} else {
checkedIndexs.push(item[key]);
}
}
});
return checkedIndexs;
}
},
watch: {
$props: {
deep: true,
handler() {
Object.assign(this.$data, initialState(this, this.expandKey));
}
}
},
updated() {
this.measure();
},
mounted() {
this.measure();
window.addEventListener("resize", this.measure);
},
beforeDestroy() {
window.removeEventListener("resize", this.measure);
}
};
</script>
<style lang="less" src="./Table.less"></style>

View File

@@ -0,0 +1,328 @@
import Checkbox from '../Checkbox/Checkbox'; // eslint-disable-line
// import Radio from '../Radio/Radio'; // eslint-disable-line
import { mixins } from './utils';
import { Radio } from 'view-design'; // eslint-disable-line
/* eslint-disable no-underscore-dangle */
export default {
name: 'TreeTable__body',
mixins: [mixins],
components: { Radio },
data() {
return {
radioSelectedIndex: -1,
};
},
computed: {
table() {
return this.$parent;
},
},
methods: {
toggleStatus(type, row, rowIndex, value) {
this.validateType(type, ['Expanded', 'Checked', 'Hide', 'Fold'], 'toggleStatus', false);
const target = this.table.bodyData[rowIndex];
this.table.bodyData.splice(rowIndex, 1, {
...target,
[`_is${type}`]: typeof value === 'undefined' ? !row[`_is${type}`] : value,
});
},
getChildrenIndex(parentLevel, parentIndex, careFold = true) {
const data = this.table.bodyData;
let childrenIndex = [];
for (let i = parentIndex + 1; i < data.length; i++) {
if (data[i]._level <= parentLevel) break;
if (data[i]._level - 1 === parentLevel) {
childrenIndex.push(i);
}
}
const len = childrenIndex.length; // important!!!
if (len > 0) {
for (let i = 0; i < len; i++) {
const childData = data[childrenIndex[i]];
if (
childData._childrenLen &&
(!careFold || (careFold && !childData._isFold))
) {
childrenIndex = childrenIndex.concat(
this.getChildrenIndex(childData._level, childrenIndex[i], careFold));
}
}
}
return childrenIndex;
},
handleEvent($event, type, data, others) {
const certainType = this.validateType(type, ['cell', 'row', 'checkbox', 'icon', 'radio'], 'handleEvent');
const eventType = $event ? $event.type : '';
const { row, rowIndex, column, columnIndex } = data;
const latestData = this.table.bodyData;
// Checkbox
if (certainType.checkbox) {
const { isChecked } = others;
this.toggleStatus('Checked', row, rowIndex, isChecked);
if (row._childrenLen > 0) {
const childrenIndex = this.getChildrenIndex(row._level, rowIndex, false);
for (let i = 0; i < childrenIndex.length; i++) {
this.toggleStatus('Checked', latestData[childrenIndex[i]], childrenIndex[i], isChecked);
}
}
return this.table.$emit('checkbox-click', latestData[rowIndex], column, columnIndex, $event);
}
// Radio
if (certainType.radio) {
this.radioSelectedIndex = rowIndex;
return this.table.$emit('radio-click', { row, rowIndex, column, columnIndex, $event });
}
// Tree's icon
if (certainType.icon) {
$event.stopPropagation();
this.toggleStatus('Fold', row, rowIndex);
const childrenIndex = this.getChildrenIndex(row._level, rowIndex);
for (let i = 0; i < childrenIndex.length; i++) {
this.toggleStatus('Hide', latestData[childrenIndex[i]], childrenIndex[i]);
}
return this.table.$emit('tree-icon-click', latestData[rowIndex], column, columnIndex, $event);
}
if (certainType.cell && eventType === 'click') {
// 点击扩展单元格
if (this.isExpandCell(this.table, columnIndex)) {
this.toggleStatus('Expanded', row, rowIndex);
return this.table.$emit('expand-cell-click', latestData[rowIndex], column, columnIndex, $event);
}
}
// 行Hover
if (certainType.row && (eventType === 'mouseenter' || eventType === 'mouseleave')) {
const { hover } = others;
const target = latestData[rowIndex];
latestData.splice(rowIndex, 1, {
...target,
_isHover: hover,
});
}
if (certainType.row && others && others.clickRow && certainType.radio) {
this.radioSelectedIndex = rowIndex;
return this.table.$emit('radio-click', { row, rowIndex, column, columnIndex, $event });
}
if (certainType.cell) {
return this.table.$emit(`${type}-${eventType}`, latestData[rowIndex], rowIndex, column, columnIndex, $event);
}
return this.table.$emit(`${type}-${eventType}`, latestData[rowIndex], rowIndex, $event);
},
},
render() {
// key
// function getKey(row, rowIndex) {
// const rowKey = this.table.rowKey;
// if (rowKey) {
// return rowKey.call(null, row, rowIndex);
// }
// return rowIndex;
// }
// style
function getStyle(type, row, rowIndex, column, columnIndex) {
const certainType = this.validateType(type, ['cell', 'row'], 'getStyle');
const style = this.table[`${type}Style`];
if (typeof style === 'function') {
if (certainType.row) {
return style.call(null, row, rowIndex);
}
if (certainType.cell) {
return style.call(null, row, rowIndex, column, columnIndex);
}
}
return style;
}
// className
function getClassName(type, row, rowIndex, column, columnIndex) {
const certainType = this.validateType(type, ['cell', 'row', 'inner'], 'getClassName');
const classList = [];
if (column && column.key == "_normalIndex") {
classList.push(`${this.prefixCls}--center-cell`);
}
// console.log(certainType.inner)
if (certainType.row || certainType.cell) {
const className = this.table[`${type}ClassName`];
if (typeof className === 'string') {
classList.push(className);
} else if (typeof className === 'function') {
if (certainType.row) {
classList.push(className.call(null, row, rowIndex) || '');
}
if (certainType.cell) {
classList.push(className.call(null, row, rowIndex, column, columnIndex) || '');
}
}
if (certainType.row) {
classList.push(`${this.prefixCls}__body-row`);
if (this.table.stripe && rowIndex % 2 !== 0) {
classList.push(`${this.prefixCls}--stripe-row`);
}
if (this.table.showRowHover && row._isHover) {
classList.push(`${this.prefixCls}--row-hover`);
}
}
if (certainType.cell) {
classList.push(`${this.prefixCls}__body-cell`);
if (this.table.border) {
classList.push(`${this.prefixCls}--border-cell`);
}
const align = column.align;
if (['center', 'right'].indexOf(align) > -1) {
classList.push(`${this.prefixCls}--${align}-cell`);
}
}
}
if (certainType.inner) {
classList.push(`${this.prefixCls}__cell-inner`);
if (this.isExpandCell(this.table, columnIndex)) {
classList.push(`${this.prefixCls}--expand-inner`);
if (row._isExpanded) {
classList.push(`${this.prefixCls}--expanded-inner`);
}
}
}
return classList.join(' ');
}
// 根据type渲染单元格Cell
function renderCell(row, rowIndex, column, columnIndex) {
// ExpandType
if (this.isExpandCell(this.table, columnIndex)) {
return <i class='zk-icon zk-icon-angle-right'></i>;
}
// SelectionType's Checkbox
if (this.isSelectionCell(this.table, columnIndex)) {
let res = null;
if (this.table.selectType === 'checkbox') {
let allCheck;
let childrenIndex;
const hasChildren = row._childrenLen > 0;
if (hasChildren) {
childrenIndex = this.getChildrenIndex(row._level, rowIndex, false);
allCheck = true;
for (let i = 0; i < childrenIndex.length; i++) {
if (!this.table.bodyData[childrenIndex[i]]._isChecked) {
allCheck = false;
break;
}
}
} else {
allCheck = row._isChecked;
}
let indeterminate = false;
if (hasChildren && !allCheck) {
for (let i = 0; i < childrenIndex.length; i++) {
if (this.table.bodyData[childrenIndex[i]]._isChecked) {
indeterminate = true;
break;
}
}
}
// res = <Checkbox
// indeterminate={indeterminate}
// value={allCheck}
// onOn-change={isChecked => this.handleEvent(null, 'checkbox', { row, rowIndex, column, columnIndex }, { isChecked })}>
// </Checkbox>;
} else {
res = <Radio value={this.radioSelectedIndex === rowIndex} on-on-change={() => this.handleEvent(null, 'radio', { row, rowIndex, column, columnIndex })}></Radio>;
}
return res;
}
// Tree's firstProp
if (this.table.treeType && this.table.firstProp === column.key) {
return <span
class={`${this.prefixCls}--level-${row._level}-cell`}
style={{
marginLeft: `${(row._level - 1) * 24}px`,
paddingLeft: row._childrenLen === 0 ? '20px' : '',
}}>
{row._childrenLen > 0 &&
<i
class={`${this.prefixCls}--tree-icon zk-icon zk-icon-${row._isFold ? 'plus' : 'minus'}-square-o`}
on-click={$event => this.handleEvent($event, 'icon', { row, rowIndex, column, columnIndex }, { isFold: row._isFold })}></i>
}
{row[column.key] ? row[column.key] : ''}
</span>;
}
// TreeType children's index
if (this.table.showIndex && this.table.treeType && column.key === '_normalIndex' && row._level > 1) {
return '';
}
if (column.type === undefined || column.type === 'custom') {
return row[column.key];
} else if (column.type === 'template') {
return this.table.$scopedSlots[column.template]
? this.table.$scopedSlots[column.template]({ row, rowIndex, column, columnIndex })
: '';
}
return '';
}
// Template
return (
<table cellspacing="0" cellpadding="0" border="0" class={`${this.prefixCls}__body`}>
{/* <colgroup>
{this.table.tableColumns.map(column =>
<col width={column.computedWidth || column.minWidth || column.width}></col>)
}
</colgroup> */}
<tbody>
{ this.table.bodyData.length > 0
? this.table.bodyData.map((row, rowIndex) =>
[
<tr
v-show={!row._isHide}
key={`table_row_${rowIndex}`}
style={getStyle.call(this, 'row', row, rowIndex)}
class={getClassName.call(this, 'row', row, rowIndex)}
on-click={$event => this.handleEvent($event, 'row', { row, rowIndex }, { clickRow: true })}
on-dblclick={$event => this.handleEvent($event, 'row', { row, rowIndex })}
on-contextmenu={$event => this.handleEvent($event, 'row', { row, rowIndex })}
on-mouseenter={$event => this.handleEvent($event, 'row', { row, rowIndex }, { hover: true })}
on-mouseleave={$event => this.handleEvent($event, 'row', { row, rowIndex }, { hover: false })}>
{ this.table.tableColumns.map((column, columnIndex) =>
column.key ? <td
style={getStyle.call(this, 'cell', row, rowIndex, column, columnIndex)}
class={getClassName.call(this, 'cell', row, rowIndex, column, columnIndex)}
on-click={$event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex })}
on-dblclick={$event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex })}
on-contextmenu={$event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex })}
on-mouseenter={$event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex })}
on-mouseleave={$event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex })}>
<div class={getClassName.call(this, 'inner', row, rowIndex, column, columnIndex)}>
{renderCell.call(this, row, rowIndex, column, columnIndex)}
</div>
</td>:"")
}
</tr>,
this.table.expandType && row._isExpanded &&
<tr
key={rowIndex}
class={`${this.prefixCls}__body-row ${this.prefixCls}--expand-row`}>
<td
class={`${this.prefixCls}--expand-content`}
colspan={this.table.tableColumns.length}>
{this.table.$scopedSlots.expand
? this.table.$scopedSlots.expand({ row, rowIndex })
: ''
}
</td>
</tr>,
])
: <tr
class={`${this.prefixCls}--empty-row`}>
<td
class={`${this.prefixCls}__body-cell ${this.prefixCls}--empty-content`}
colspan={this.table.tableColumns.length}>
{this.table.emptyText}
</td>
</tr>
}
</tbody>
</table>
);
},
};

View File

@@ -0,0 +1,84 @@
import { mixins } from './utils';
/* eslint-disable no-underscore-dangle */
export default {
name: 'TreeTable__footer',
mixins: [mixins],
data() {
return {
};
},
computed: {
table() {
return this.$parent;
},
},
methods: {
},
render() {
// 计算各列总和
function renderCell({ key }, columnIndex) {
if (columnIndex === 0) {
return this.table.sumText;
}
const rows = this.table.bodyData;
const values = rows.map(row => Number(row[key]));
const precisions = [];
let notNumber = true;
values.forEach((value) => {
if (!isNaN(value)) {
notNumber = false;
const decimal = value.toString().split('.')[1];
precisions.push(decimal ? decimal.length : 0);
}
});
const precision = Math.max.apply(null, precisions);
if (!notNumber) {
return values.reduce((prev, curr) => {
const value = Number(curr);
if (!isNaN(value)) {
return parseFloat((prev + curr).toFixed(precision));
}
return prev;
}, 0);
}
return '';
}
// className
function getClassName() {
const classList = [];
classList.push(`${this.prefixCls}__footer-cell`);
if (this.table.border) {
classList.push(`${this.prefixCls}--border-cell`);
}
return classList.join(' ');
}
// Template
return (
<table cellspacing="0" cellpadding="0" border="0" class={ `${this.prefixCls}__footer` }>
<colgroup>
{ this.table.tableColumns.map(column =>
<col width={ column.computedWidth || column.minWidth || column.width }></col>)
}
</colgroup>
<tfoot>
<tr class={ `${this.prefixCls}__footer-row` }>
{ this.table.tableColumns.map((column, columnIndex) =>
<td class={ getClassName.call(this) }>
<div class={ `${this.prefixCls}__cell-inner` }>
{ this.table.summaryMethod
? this.table.summaryMethod(this.table.bodyData, column, columnIndex)
: renderCell.call(this, column, columnIndex) }
</div>
</td>)
}
</tr>
</tfoot>
</table>
);
},
};

View File

@@ -0,0 +1,108 @@
import Checkbox from "../Checkbox/Checkbox"; // eslint-disable-line
import { mixins } from "./utils";
/* eslint-disable no-underscore-dangle */
export default {
name: "TreeTable__header",
mixins: [mixins],
data() {
return {};
},
computed: {
table() {
return this.$parent;
}
},
methods: {
toggleAllChecked(checked) {
this.table.bodyData = this.table.bodyData.map(row => ({
...row,
_isChecked: checked
}));
}
},
render() {
// className
function getClassName(type, { headerAlign, key }) {
const certainType = this.validateType(
type,
["cell", "inner"],
"getClassName"
);
const classList = [];
if (key == "_normalIndex") {
classList.push(`${this.prefixCls}--center-cell`);
}
if (certainType.cell) {
classList.push(`${this.prefixCls}__header-cell`);
if (this.table.border) {
classList.push(`${this.prefixCls}--border-cell`);
}
if (["center", "right"].indexOf(headerAlign) > -1) {
classList.push(`${this.prefixCls}--${headerAlign}-cell`);
}
}
if (certainType.inner) {
classList.push(`${this.prefixCls}__cell-inner`);
if (this.table.treeType && this.table.firstProp === key) {
classList.push(`${this.prefixCls}--firstProp-header-inner`);
}
}
return classList.join(" ");
}
// 根据type渲染单元格Label
function renderLabel(column, columnIndex) {
if (
this.isSelectionCell(this.table, columnIndex) &&
this.selectType === "checkbox"
) {
const allCheck = this.table.bodyData.every(row => row._isChecked);
const indeterminate =
!allCheck && this.table.bodyData.some(row => row._isChecked);
return (
<Checkbox
indeterminate={indeterminate}
value={allCheck}
onOn-change={checked => this.toggleAllChecked(checked)}
></Checkbox>
);
}
return column.title ? column.title : "";
}
// Template
return (
<table
cellspacing="0"
cellpadding="0"
border="0"
class={`${this.prefixCls}__header`}
>
{/* <colgroup>
{this.table.tableColumns.map(column => (
<col
width={column.computedWidth || column.minWidth || column.width}
></col>
))}
</colgroup> */}
<thead>
<tr class={`${this.prefixCls}__header-row`}>
{
this.table.tableColumns.map((column, columnIndex) =>
column.key ? (
<th class={getClassName.call(this, "cell", column)}>
<div class={getClassName.call(this, "inner", column)}>
{renderLabel.call(this, column, columnIndex)}
</div>
</th>
) : (
""
)
)}
</tr>
</thead>
</table>
);
}
};

View File

@@ -0,0 +1,22 @@
// icon
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1505310522875'); /* IE9*/
src: url('iconfont.eot?t=1505310522875#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAW0AAsAAAAACOQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW7kggY21hcAAAAYAAAABuAAABojLtBtFnbHlmAAAB8AAAAa8AAAKA73SzT2hlYWQAAAOgAAAALwAAADYO3fRqaGhlYQAAA9AAAAAcAAAAJAfeA4ZobXR4AAAD7AAAABMAAAAUE+kAAGxvY2EAAAQAAAAADAAAAAwBbAHYbWF4cAAABAwAAAAeAAAAIAEUAF1uYW1lAAAELAAAAUUAAAJtPlT+fXBvc3QAAAV0AAAAQAAAAFryy5h0eJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2Bk/s04gYGVgYOpk+kMAwNDP4RmfM1gxMjBwMDEwMrMgBUEpLmmMDgwVDwzZm7438AQw9zA0AAUZgTJAQAoHgyieJzFkcENwyAUQ98HyqHKKDmEZoEOklOnYOK/RmI+uXSCWDLG/pZAALyALK5iAfthDBxKLfLMO/LCJl+lRqL7fp7y3VuoKprV0KxO0qbyGOy5o/+xxPq9nV6YflNX9DY5fsA/k6Pj+yTpAn3jEO8AAHiclVDNitNQFD7n3slNE9vE5N7kpOn0J0mbKB3DGDMZRGw3bhQXA2LB5TyAbmfjohvBhQvfYEAEoc8wr+EDiK4KPkITU0EcXDmHw3fOgfN9fHygATTf+BUPQMIduA9P4AwAxRxjiw0xysqczdGLNI+UxbMki/QkzvljpFgov6jKlIQubLRwhA+iospyluFJuWCPsPCHiP1B+MKdHbr8I5pBNnpXP2Of0Bsnh/biXv30aKmKiexcdF2377ofOkLTOowd2Ba+Jt/QDFPUnzU79K7Gd9kYu/0sfP6qNxm45+/LN8MZGYjrNcrBxPqydEKn7behL92+frvXCcJeMlV48eNWILvD9Du0hXtgl+wSHIBZnJZLzNKyKgh9paPAdVca260hAxPBMBowz9t1uzUDuT8CswEDDtq8Gr7mADaM4QgetrKRhbozQooWeOrkKJVIojg9ccqqjcT3uBJ6lGNZnUYjnBU+ORbGaeYskM93m+kx4vGUrX5PQXK3kUSSrSS9JFWvFJHCjaIGJCFSugcOLeM6c7f6wyFZf/37+PO6AgD/x/vNnN/A7H8bhF86tmcbAHicY2BkYGAAYl/p/jnx/DZfGbhZGEDg6v0IKwT9/yELA7MEkMvBwAQSBQAZYgnZAHicY2BkYGBu+N/AEMPCAAJAkpEBFbACAEcLAm54nGNhYGBgfsnAwMKAwAAOmwD9AAAAAAAAdgCYAPYBQHicY2BkYGBgZQgEYhBgAmIuIGRg+A/mMwAAES0BcgAAeJxlj01OwzAQhV/6B6QSqqhgh+QFYgEo/RGrblhUavdddN+mTpsqiSPHrdQDcB6OwAk4AtyAO/BIJ5s2lsffvHljTwDc4Acejt8t95E9XDI7cg0XuBeuU38QbpBfhJto41W4Rf1N2MczpsJtdGF5g9e4YvaEd2EPHXwI13CNT+E69S/hBvlbuIk7/Aq30PHqwj7mXle4jUcv9sdWL5xeqeVBxaHJIpM5v4KZXu+Sha3S6pxrW8QmU4OgX0lTnWlb3VPs10PnIhVZk6oJqzpJjMqt2erQBRvn8lGvF4kehCblWGP+tsYCjnEFhSUOjDFCGGSIyujoO1Vm9K+xQ8Jee1Y9zed0WxTU/3OFAQL0z1xTurLSeTpPgT1fG1J1dCtuy56UNJFezUkSskJe1rZUQuoBNmVXjhF6XNGJPyhnSP8ACVpuyAAAAHicY2BigAAuBuyAlZGJkZmRhZGVkY2BsYI7MS89J1W3KDM9o4S3IKe0WLe4sDSxKFU3ny83Mw+Jy8AAAHSWD8A=') format('woff'),
url('iconfont.ttf?t=1505310522875') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('iconfont.svg?t=1505310522875#iconfont') format('svg'); /* iOS 4.1- */
}
.zk-icon {
font-family: "iconfont" !important;
font-size: 14px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.zk-icon-plus-square-o:before { content: "\e631"; }
.zk-icon-minus-square-o:before { content: "\e632"; }
.zk-icon-angle-right:before { content: "\e633"; }

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
<font-face
font-family="iconfont"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
ascent="896"
descent="-128"
/>
<missing-glyph />
<glyph glyph-name="x" unicode="x" horiz-adv-x="1001"
d="M281 543q-27 -1 -53 -1h-83q-18 0 -36.5 -6t-32.5 -18.5t-23 -32t-9 -45.5v-76h912v41q0 16 -0.5 30t-0.5 18q0 13 -5 29t-17 29.5t-31.5 22.5t-49.5 9h-133v-97h-438v97zM955 310v-52q0 -23 0.5 -52t0.5 -58t-10.5 -47.5t-26 -30t-33 -16t-31.5 -4.5q-14 -1 -29.5 -0.5
t-29.5 0.5h-32l-45 128h-439l-44 -128h-29h-34q-20 0 -45 1q-25 0 -41 9.5t-25.5 23t-13.5 29.5t-4 30v167h911zM163 247q-12 0 -21 -8.5t-9 -21.5t9 -21.5t21 -8.5q13 0 22 8.5t9 21.5t-9 21.5t-22 8.5zM316 123q-8 -26 -14 -48q-5 -19 -10.5 -37t-7.5 -25t-3 -15t1 -14.5
t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-5 19 -11 39h-368zM336 498v228q0 11 2.5 23t10 21.5t20.5 15.5t34 6h188q31 0 51.5 -14.5t20.5 -52.5v-227h-327z" />
<glyph glyph-name="angle-right" unicode="&#58931;" d="M384.087 97.474c-7.857 0-15.713 2.997-21.707 8.992-11.989 11.989-11.989 31.426 0 43.415l234.118 234.12-234.118 234.118c-11.988 11.989-11.988 31.427 0 43.416 11.989 11.988 31.426 11.988 43.416 0l255.826-255.827c11.989-11.989 11.989-31.427 0-43.416l-255.826-255.827c-5.995-5.995-13.851-8.992-21.708-8.992z" horiz-adv-x="1024" />
<glyph glyph-name="plus-square-o" unicode="&#58929;" d="M810.666667 768H213.333333c-46.933333 0-85.333333-38.4-85.333333-85.333333v-597.333334c0-46.933333 38.4-85.333333 85.333333-85.333333h597.333334c46.933333 0 85.333333 38.4 85.333333 85.333333V682.666667c0 46.933333-38.4 85.333333-85.333333 85.333333z m42.666666-682.666667c0-25.6-17.066667-42.666667-42.666666-42.666666H213.333333c-25.6 0-42.666667 17.066667-42.666666 42.666666V682.666667c0 25.6 17.066667 42.666667 42.666666 42.666666h597.333334c25.6 0 42.666667-17.066667 42.666666-42.666666v-597.333334zM768 384c0-25.6-17.066667-42.666667-42.666667-42.666667H298.666667c-25.6 0-42.666667 17.066667-42.666667 42.666667s17.066667 42.666667 42.666667 42.666667h426.666666c25.6 0 42.666667-17.066667 42.666667-42.666667zM512 640c25.6 0 42.666667-17.066667 42.666667-42.666667v-426.666666c0-25.6-17.066667-42.666667-42.666667-42.666667s-42.666667 17.066667-42.666667 42.666667V597.333333c0 25.6 17.066667 42.666667 42.666667 42.666667z" horiz-adv-x="1024" />
<glyph glyph-name="minus-square-o" unicode="&#58930;" d="M810.666667 768H213.333333c-46.933333 0-85.333333-38.4-85.333333-85.333333v-597.333334c0-46.933333 38.4-85.333333 85.333333-85.333333h597.333334c46.933333 0 85.333333 38.4 85.333333 85.333333V682.666667c0 46.933333-38.4 85.333333-85.333333 85.333333z m42.666666-682.666667c0-25.6-17.066667-42.666667-42.666666-42.666666H213.333333c-25.6 0-42.666667 17.066667-42.666666 42.666666V682.666667c0 25.6 17.066667 42.666667 42.666666 42.666666h597.333334c25.6 0 42.666667-17.066667 42.666666-42.666666v-597.333334zM768 384c0-25.6-17.066667-42.666667-42.666667-42.666667H298.666667c-25.6 0-42.666667 17.066667-42.666667 42.666667s17.066667 42.666667 42.666667 42.666667h426.666666c25.6 0 42.666667-17.066667 42.666667-42.666667z" horiz-adv-x="1024" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,6 @@
import mixins from './mixins';
import scrollBarWidth from './scrollBarWidth';
export {
mixins,
scrollBarWidth
}

View File

@@ -0,0 +1,34 @@
export default {
data() {
return {
prefixCls: 'zk-table',
};
},
methods: {
validateType(type, validTypes, funcName, isReturn = true) {
if (validTypes.indexOf(type) < 0) throw new Error(`${funcName}'s type must is ${validTypes.join(' or ')}.`);
if (isReturn) {
const certainType = {};
validTypes.forEach((item) => {
certainType[item] = item === type;
});
return certainType;
}
return true;
},
isExpandCell(table, columnIndex) {
return table.expandType && (
(table.showIndex && columnIndex === 1) ||
(!table.showIndex && columnIndex === 0)
);
},
isSelectionCell(table, columnIndex) {
return table.selectable && (
(table.showIndex && table.expandType && columnIndex === 2) ||
(!table.showIndex && table.expandType && columnIndex === 1) ||
(table.showIndex && !table.expandType && columnIndex === 1) ||
(!table.showIndex && !table.expandType && columnIndex === 0)
);
},
},
};

View File

@@ -0,0 +1,28 @@
import Vue from 'vue';
let scrollBarWidth;
export default function () {
if (Vue.prototype.$isServer) return 0;
if (scrollBarWidth !== undefined) return scrollBarWidth;
const outer = document.createElement('div');
outer.style.visibility = 'hidden';
outer.style.width = '100px';
outer.style.position = 'absolute';
outer.style.top = '-9999px';
document.body.appendChild(outer);
const widthNoScroll = outer.offsetWidth;
outer.style.overflow = 'scroll';
const inner = document.createElement('div');
inner.style.width = '100%';
outer.appendChild(inner);
const widthWithScroll = inner.offsetWidth;
outer.parentNode.removeChild(outer);
scrollBarWidth = widthNoScroll - widthWithScroll;
return scrollBarWidth;
}

View File

@@ -0,0 +1,14 @@
### 滑动拼图验证
### 在页面中引入 .vue文件
#### 参数
#### 在组件上添加v-if来判断组件显隐
#### verifyType 验证格式[ 'LOGIN' ,'REGISTER' ]
#### @change方法 获取回调,参数为对象 {status:false,distance:100} status 为回调状态distance为移动距离
#### <Verify class="verify-content" verifyType='LOGIN' @change="verifyChange"></Verify>

View File

@@ -0,0 +1,191 @@
<template>
<div class="verify-content" v-if="show" @mousemove="mouseMove" @mouseup="mouseUp" @click.stop>
<div class="imgBox" :style="{width:data.originalWidth+'px',height:data.originalHeight + 'px'}">
<img :src="data.backImage" style="width:100%;height:100%" alt="">
<img class="slider" :src="data.slidingImage" :style="{left:distance+'px',top:data.randomY+'px'}" :width="data.sliderWidth" :height="data.sliderHeight" alt="">
<Icon type="md-refresh" class="refresh" @click="init" />
</div>
<div class="handle" :style="{width:data.originalWidth+'px'}">
<span class="bgcolor" :style="{width:distance + 'px',background:bgColor}"></span>
<span class="swiper" :style="{left:distance + 'px'}" @mousedown="mouseDown">
<Icon type="md-arrow-round-forward" />
</span>
<span class="text">{{verifyText}}</span>
</div>
</div>
</template>
<script>
import { getVerifyImg, postVerifyImg } from './verify.js';
export default {
props: {
// 传入数据,判断是登录、注册、修改密码
verifyType: {
defalut: 'LOGIN',
type: String
}
},
data () {
return {
show: false, // 验证码显隐
type: 'LOGIN', // 请求类型
data: { // 验证码数据
backImage: '',
slidingImage: '',
originalHeight: 150,
originalWidth: 300,
sliderWidth: 60,
sliderHeight: 60
},
distance: 0, // 拼图移动距离
flag: false, // 判断滑块是否按下
downX: 0, // 鼠标按下位置
bgColor: '#04ad11', // 滑动背景颜色
verifyText: '拖动滑块解锁' // 文字提示
};
},
methods: {
// 鼠标按下事件,开始拖动滑块
mouseDown (e) {
this.downX = e.clientX;
this.flag = true;
},
// 鼠标移动事件,计算距离
mouseMove (e) {
if (this.flag) {
let offset = e.clientX - this.downX;
if (offset > this.data.originalWidth - 43) {
this.distance = this.data.originalWidth - 43;
} else if (offset < 0) {
this.distance = 0;
} else {
this.distance = offset;
}
}
},
// 鼠标抬起事件,验证是否正确
mouseUp () {
if (!this.flag) return false;
this.flag = false;
let params = {
verificationEnums: this.type,
xPos: this.distance
};
postVerifyImg(params).then(res => {
if (res.success) {
if (res.result) {
this.bgColor = 'green';
this.verifyText = '解锁成功';
this.$emit('change', { status: true, distance: this.distance });
} else {
this.bgColor = 'red';
this.verifyText = '解锁失败';
let that = this;
setTimeout(() => {
that.init();
}, 1000);
this.$emit('change', { status: false, distance: this.distance });
}
} else {
this.init()
}
}).catch(()=>{
this.init()
});
},
init () { // 初始化数据
this.flag = false;
this.downX = 0;
this.distance = 0;
this.bgColor = '#04ad11';
this.verifyText = '拖动滑块解锁';
getVerifyImg(this.type).then(res => {
if (res.result) {
this.data = res.result;
this.show = true;
} else {
this.$Message.warning('请求失败请重试!')
}
});
}
},
watch: {
verifyType: {
immediate: true,
handler: function (v) {
this.type = v;
}
}
}
};
</script>
<style lang="scss" scoped>
.verify-content{
padding: 10px;
background: #fff;
border: 1px solid #eee;
border-radius: 5px;
box-shadow: 1px 1px 3px #999;
}
.imgBox {
width: 300px;
height: 150px;
position: relative;
overflow: hidden;
.slider {
position: absolute;
cursor: pointer;
}
.refresh {
position: absolute;
right: 5px;
top: 5px;
font-size: 20px;
color: #fff;
cursor: pointer;
}
}
.handle {
border: 1px solid #e4dede;
margin-top: 5px;
height: 42px;
background: #ddd;
position: relative;
.bgcolor {
position: absolute;
top: 0;
left: 0;
width: 40px;
height: 40px;
opacity: 0.5;
background: #04ad11;
}
.swiper {
position: absolute;
width: 40px;
height: 40px;
background-color: #fff;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
.ivu-icon {
font-size: 20px;
}
}
.text {
display: inline-block;
width: inherit;
text-align: center;
line-height: 42px;
font-size: 14px;
user-select: none;
}
}
</style>

View File

@@ -0,0 +1,13 @@
import {commonUrl, getRequestWithNoToken, postRequestWithNoToken} from '@/libs/axios';
// 获取拼图验证
export const getVerifyImg = (verificationEnums) => {
return getRequestWithNoToken(`${commonUrl}/common/common/slider/${verificationEnums}`);
};
// 拼图验证
export const postVerifyImg = (params) => {
return postRequestWithNoToken(`${commonUrl}/common/common/slider/${params.verificationEnums}`, params);
};