Commit 4fb26a51 by 曾经

Merge branch 'feature/7月基础一异常冻结会员' into test

* feature/7月基础一异常冻结会员: (50 commits)
  无用方法删除
  UI
  冻结解冻 新增字段 pageType
  白名单 宽度
  table-sticky
  iconfont文件提交
  冻结方式 UI
  link 公共样式 引入注释
  输入框 可清空
  部分电脑 标题显示点点点的问题
  异常会员 冻结会员 状态切换 全部选项删除,该选择可清空
  冻结类型
  bug 修复-列表进详情 返回刷新
  导入文件大小限制,以及提示
  需求变更-默认最小值更改
  no message
  生日 格式化
  bug 修复
  页面缓存
  异常规则配置提交校验
  ...

# Conflicts:
#	src/components/allCustomers/index.js
#	src/components/axios/url.js
parents f4767f59 0ea3419a
......@@ -6,7 +6,8 @@
<style href="//at.alicdn.com/t/font_2996579_ubjq74uy5wj.css"></style><!--GIC3.0 客户 -->
<!-- 3.0 组件库 -->
<script src="//at.alicdn.com/t/font_2859043_ckil7xvsqi.js"></script>
<!-- <style href="//at.alicdn.com/t/font_688955_2dxzdzrb3a7.css"></style>GIC后台3.0 -->
<!-- <link rel="stylesheet" href="//web-1251519181.file.myqcloud.com/simple-style.1.0.2.css"></link> -->
<!-- <link href="//at.alicdn.com/t/font_688955_2dxzdzrb3a7.css"></link>GIC后台3.0 -->
<!-- <script src="//at.alicdn.com/t/font_688955_2dxzdzrb3a7.js"></script> -->
<script src="//web-1251519181.file.myqcloud.com/lib/lodash.min.js"></script>
<title></title>
......
......@@ -125,21 +125,29 @@
}
}
}
.customer-info-cell {
display: flex;
align-items: center;
img {
display: block;
width: 60px;
height: 60px;
margin-right: 10px;
// border-radius: 100%;
}
.memberName {
overflow: hidden;
.basic-info-table {
color: #303133;
font-size: 14px;
.member-name{
width: 140px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.nick-name {
color: #909399;
font-size: 14px;
}
.basic-img {
display: inline-block;
vertical-align: middle;
height: 40px;
width: 40px;
min-width: 40px;
border-radius: 50%;
overflow: hidden;
margin-right: 10px;
}
}
.status-icon {
font-size: 20px;
......
......@@ -159,12 +159,29 @@
>
<template slot-scope="{row}">
<!-- 基本信息 -->
<div v-if="colum == 'name'" :class="['customer-info-cell', getCodeAuth('memberIntoCustomDetail') && 'pointer']" @click="linkDetail(row.memberId)">
<img :src="row.thirdImgUrl||defaultImg" alt="">
<p class="memberName">
{{ row.memberName || '--' }}
</p>
</div>
<el-row
v-if="colum == 'name'"
type="flex"
align="middle"
class="basic-info-table"
@click.native="linkDetail(row.memberId)"
>
<img :src="row.thirdImgUrl || defaultImg" class="basic-img" />
<div>
<div class="member-name">
<span v-if="row.memberName">{{
row.memberName
}}</span>
<span class="nick-name" v-if="row.nickName"
>({{ row.nickName }})</span
>
</div>
<div v-if="row.phoneNumber" class="phone-number">
{{ row.phoneNumber }}
</div>
</div>
</el-row>
<!-- 等级 -->
<span v-else-if="colum == 'grade'">{{ row.gradeName }}</span>
<!-- 状态 -->
......@@ -348,7 +365,8 @@
@successImport="successImport"
/>
<!-- 批量导入 -->
<import-dialog :dialogVisible.sync="dialogImportVisible" @successImport="taskId => successImport(taskId, true)"/>
<import-dialog :dialogVisible.sync="dialogImportVisible"
:importList="importList" @successImport="taskId => successImport(taskId, true)"/>
</div>
</template>
<script>
......
......@@ -5,8 +5,8 @@ import gradeDailog from "./components/batchgradeDialog.vue";
import mainstoreDailog from "./components/mainstoreDialog.vue";
import substoreDailog from "./components/substoreDialog.vue";
import batchList from "./components/batchList.vue";
import importDialog from "./components/importDialog.vue";
import { doFetch, doFetchGet } from "../../components/axios/api";
import importDialog from "@/components/allCustomers/components/importDialog.vue";
import url from "../../components/axios/url";
import { mapState } from "vuex";
import { formatLongTime, paddingBorth } from "@/utils/utils";
......@@ -46,6 +46,32 @@ export default {
searchAble: false,
multipleAble: true
},
importList:[
{
importType: 11,
importValue: "积分增加",
},
{
importType: 12,
importValue: "积分扣除",
},
{
importType: 13,
importValue: "等级调整",
},
{
importType: 14,
importValue: "服务门店/专属导购调整",
},
{
importType: 15,
importValue: "批量冻结会员",
},
{
importType: 16,
importValue: "批量解冻会员",
},
],
sceneValue: "member", // 人群筛选器场景值
navpath: [
{ name: "首页", path: "" },
......@@ -222,6 +248,9 @@ export default {
this.getAjaxMembers();
this.getEnterpriseInfo();
},
activated(){
this.getAjaxMembers();
},
beforUpdate() {},
methods: {
onSelectTreeChange(data) {
......
......@@ -8,6 +8,7 @@ export function doFetchqs(url,option) {
}
export function doFetchGet(url, option) {
option = option || {};
option.requestProject = 'member';
return fetchGet(url, option);
}
......@@ -109,7 +109,20 @@ const urlConfig = {
batchUpdateFrozen: "/api-member/members-batch-update-frozen",
getExceptionList: '/api-member/list-exception-member',
getErpIntegral: '/api-member/member-erp-integral'
getErpIntegral: '/api-member/member-erp-integral',
getExceptionMemberList: '/api-member/exception-member-list',// 获取异常列表
exceptionMemberExportExcel: '/api-admin/exception-member-export-excel',// 异常会员数据导出
exceptionMemberBatchFrozen: '/api-member/exception-batch-frozen',// 异常会员批量冻结
getAbnormalSetting: '/api-member/exception-config-view',// 异常配置详情
saveAbnormalSetting: '/api-member/exception-config-save',// 保存异常配置
getUnfreezeCount: '/api-member/no_froze_member_count',// 获取当前选中的异常会员的未冻结数量
getEnableDownload:'/api-auth/get-enable-download-data-config-by-user',
updateFrozen: '/api-member/exception-frozen',// 异常更新冻结状态
getMembersForFrozen:'/api-member/ajax-members-do-frozen',
getFrozenWhiteList: '/api-member/white-member-list',//获取冻结白名单列表
removeFrozenWhiteMember:'/api-member//white-member-remove',// 移出白名单
frozenMemberExportExcel: '/api-admin/frozen-member-export-excel',// 异常会员搜索结果导出
}
const defaultUrl = Object.assign({}, urlConfig);
......
.forzen-container {
padding: 20px;
background-color: #fff;
.search {
margin-bottom: 24px;
.top-header {
display: flex;
align-items: center;
margin-bottom: 20px;
h2 {
margin-right: 20px;
font-size: 16px;
font-weight: bold;
color: #303133;
line-height: 22px;
}
}
.search-item {
display: flex;
justify-content: space-between;
}
}
.basic-info-table {
color: #303133;
font-size: 14px;
.member-name{
width: 140px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.nick-name {
color: #909399;
font-size: 14px;
}
.basic-img {
display: inline-block;
vertical-align: middle;
height: 40px;
width: 40px;
min-width: 40px;
border-radius: 50%;
overflow: hidden;
margin-right: 10px;
}
}
}
.frozen-table {
.pager {
text-align: right;
padding: 20px 0 0;
}
/deep/.el-table-column--selection {
.cell {
padding-left: 16px;
padding-right: 0px;
}
}
/deep/.cell {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/deep/.table-select-page .cell {
padding-left: 10px;
padding-right: 0;
display: flex;
align-items: center;
.el-dropdown {
padding: 0;
}
}
}
.frozen-list {
height: 100%;
}
.no-setting-page {
height: 100%;
position: relative;
display: flex;
align-items: center;
justify-content: center;
.tips-box {
height: auto;
margin-right: 150px;
.label {
color: #0a1c2b;
font-size: 20px;
font-weight: 500;
}
.el-button--primary {
margin-top: 40px;
border-radius: 2px;
}
}
}
.no-update-page {
position: relative;
text-align: center;
height: calc(100% - 100px);
padding: 0 30px;
.icon {
width: 120px;
height: 120px;
margin: 216px auto 16px;
}
.label {
color: #606266;
font-size: 14px;
}
.no-update-alert{
width: auto;
margin-top: 15px;
/deep/.el-icon-info{
color: #2F54EB;
}
}
}
.data-abnormal-page{
position: relative;
text-align: center;
height: 100%;
padding: 0 30px;
.icon{
margin: 116px auto 30px;
color: #FA8C16;
width: 72px;
height: 72px;
font-size: 72px;
}
.title{
color: #303133;
font-size: 14px;
line-height: 20px;
}
.subtitle{
color: #606266;
font-size: 14px;
line-height: 20px;
}
.el-button--primary{
margin: 30px auto;
}
}
.channel-box {
display: inline-block;
+ .channel-box {
margin-left: 6px;
}
.channel-icon {
width: 20px;
height: 20px;
vertical-align: middle;
}
}
.frozen-table /deep/.el-table {
.setting-cell.el-table__cell {
display: flex;
align-items: center;
height: 65px;
padding: 0;
.cell.setting-cell {
display: flex;
justify-content: space-between;
align-items: center;
height: 65px;
padding-right: 0;
.el-icon-setting {
display: flex;
justify-content: center;
align-items: center;
width: 60px;
height: 100%;
font-size: 20px;
color: #666;
cursor: pointer;
background: #eaeaea;
&:hover {
color: #000;
}
}
}
}
/deep/.el-table__empty-block {
z-index: 1;
position: relative;
background: white;
}
}
.abnormal-setting-page-box{
position: relative;
.abnormal-setting-page{
padding: 20px;
.el-alert--info{
align-items: baseline;
&.is-light{
background: #F7F8FA;
color: #303133;
}
/deep/.el-alert__icon{
color: #2F54EB;
}
/deep/.el-alert__title{
white-space: pre-wrap;
}
}
.el-form{
margin-top: 11px;
/deep/.el-form-item__label{
}
}
.tips{
color: #606266;
font-size: 12px;
margin-top: 6px;
&.light{
color: #2F54EB;
}
&.points{
position: relative;
&::before{
content: " ";
position: absolute;
width: 4px;
height: 4px;
border-radius: 50%;
background: #909399;
top: 50%;
transform: translateY(-50%);
left: -8px;
}
}
}
.dashed-line{
margin: 24px 0 20px;
border-bottom: 1px dashed #DCDFE6;
}
.input-form-item{
margin: 0 10px;
}
.count-input{
width: 140px;
}
.amount-input{
width: 140px;
}
.el-input-number{
text-align: left;
/deep/.el-input{
text-align: left;
/deep/.el-input__inner{
text-align: left;
}
}
}
.cell-item{
margin-top: 20px;
padding: 20px;
border-radius: 4px;
border: 1px solid #E4E7ED;
.label{
color: #303133;
font-size: 14px;
}
}
.form-item-cell{
padding: 10px 0;
&:first-child{
padding-top: 0;
}
&:last-child{
padding-bottom: 0;
}
}
}
.page-bottom{
position: sticky;
z-index: 10;
bottom: 0;
left: 0;
width: 100%;
height: 56px;
background: white;
box-shadow: 1px -2px 8px 0px rgba(220,223,230,0.6000);
}
}
<template>
<el-input-number
v-model="value"
:min="min"
:max="max"
:step="step"
:precision="precision"
:controls="false"
step-strictly
:placeholder="min + '-' + max"
@change="change"
></el-input-number>
</template>
<script>
export default {
name: 'FreezeInput',
components: {},
props: {
value:'',
min: '',
max: '',
step: '',
precision: '',
},
data() {
return {
};
},
methods: {
change(value){
console.log("value---->",value);
this.$emit('update:value', value);
}
},
watch: {
}
};
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<el-dialog
title="设置显示字段"
custom-class="table-colum-dailog"
:visible.sync="visible"
@close="cancel"
width="600px"
>
<div class="checkwtip mBottom10">
请选择您想显示的列表字段,最多勾选{{ max }}个选项,已经勾选了 <span
class="curcheck"
>{{ checkedFieldsCopy.length }}</span>
</div>
<el-checkbox-group
v-model="checkedFieldsCopy"
class="table-colum-wrap"
:min="min"
:max="max"
>
<el-checkbox
v-for="(item, index) in fields"
class="table-colum-item"
:key="index"
:label="item"
:disabled="item.code==='name' || item.code==='memberName'"
>
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
<span slot="footer" class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="submitFields">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'ExportFieldsDialog',
props: {
min: 0,
max: 8,
fields: {
type: Array,
default: () => {
return [];
},
},
dialogVisible: {
type: Boolean,
default: false,
},
},
data () {
return {
visible: false,
checkedFieldsCopy: [],
};
},
watch: {
dialogVisible (n, o) {
this.visible = n;
if(!(this.fields && this.fields.length)){
this.$message({
message: "设置异常",
type: "error"
});
return this.visible = false;
}
if (n) {
this.checkedFieldsCopy = this.fields.filter((item)=>{
return item.check
});
}
},
},
methods: {
submitFields () {
this.$emit('update:dialogVisible', false);
this.$emit('submit', this.checkedFieldsCopy.map((item)=>{
return item.code;
}).join(','));
},
cancel () {
this.$emit('update:dialogVisible', false);
},
},
};
</script>
<style lang="less" scoped>
.table-colum-wrap {
display: flex;
flex-wrap: wrap;
width: 100%;
.table-colum-item {
flex: 25%;
line-height: 35px;
}
.table-colum-item + .table-colum-item {
margin-left: 0;
}
}
</style>
<style lang="less">
// 去除dailog-footer上边框
.table-colum-dailog .el-dialog__footer {
border-top: none !important;
}
.table-colum-dailog .el-checkbox:last-of-type {
margin-right: 30px;
}
</style>
<template>
<div>
<el-dialog :title="title || (isBatchFreeze ? '批量冻结会员' : '冻结会员')"
:visible.sync="visible" width="600px" @close="onClose">
<div v-if="isBatchFreeze" class="freeze-tips">
<span>当前已选择</span>
<span class="count"> {{ count }} </span>
<span>位未冻结的会员</span>
</div>
<el-form :model="submitData" ref="submitForm" :rules="rules">
<el-form-item label="冻结原因" prop="frozenType" required>
<el-select v-model="submitData.frozenType" placeholder="" :disabled="isAbnormalFreeze"
style="width: 550px;">
<el-option label="异常冻结" :value="1"></el-option>
<el-option label="会员主动冻结" :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="原因备注" prop="remark" required>
<el-input v-model="submitData.remark" placeholder="请输入原因备注" maxlength='20'
show-word-limit></el-input>
</el-form-item>
</el-form>
<el-row type="flex" justify="end">
<el-button @click="onClose">取消</el-button>
<el-button type="primary" @click="sure('submitForm')">确定</el-button>
</el-row>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'FreezeDialog',
components: {},
props: {
submit: {
type: Function
},
visible: false,
count: 0,
isBatchFreeze: false,// 是否批量冻结
title: '',
isAbnormalFreeze: false,
},
data() {
return {
rules:{
remark:{ required: true, message: '请输入原因备注' }
},
submitData:{
frozenType: 1,
remark: '',
},
};
},
methods: {
onClose() {
this.$emit('update:visible', false);
},
sure(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.submit && this.submit(this.submitData);
this.$emit('submit', this.submitData);
} else {
console.log('error submit!!');
return false;
}
});
// console.log(this.dateValue,getTime(this.dateValue[0]));
}
},
watch: {
}
};
</script>
<style lang="scss" scoped>
/deep/.el-dialog__body{
padding-top: 14px;
}
.freeze-tips{
margin-bottom: 20px;
color: #6B6D71;
font-size: 14px;
.count{
font-size:16px;
color: #37383A;
font-weight: 500;
}
}
</style>
import { doFetch, doFetchGet } from "@/components/axios/api";
import url from "@/components/axios/url";
import {
checkFalse,
checkSuccess
} from "../../../../../static/js/checkStatus";
import authMethods from "@/mixins/auth";
import defaultImg from "../../../../../static/img/default.png";
import { formatLongTime,paddingBorth } from "@/utils/utils";
import FreezeDialog from "../../components/dialog/freeze.vue";
import MemberFieldsDialog from "../../components/dialog/fieldsSettingDialog.vue";
import ExportFieldsDialog from "../../components/dialog/fieldsSettingDialog.vue";
export default {
name: "forzenlist",
components: {
ExportFieldsDialog,
MemberFieldsDialog,
FreezeDialog
},
mixins: [authMethods],
filters: {
formatBirth(value){
return paddingBorth(value) || '--'
},
formatDate(val, format) {
if (!val || val == -1) return format == "hms" ? "" : "--";
let str = formatLongTime(val, 1);
let arr = str.split(" ");
if (format == "ymd") {
return arr[0];
} else if (format == "hms") {
return arr[1] || "";
}
return str;
}
},
data() {
return {
freezeDialogOptions: {
count: 1,
isBatchFreeze: 0,
visible: false
},
exportData: {
projectName: "member", // 当前项目名
dialogVisible: false,
type: 1,
excelUrl: url.frozenMemberExportExcel, // 下载数据的地址
params: {} // 传递的参数
},
dialogMemberFieldVisible: false,
dialogExportFieldVisible: false,
defaultImg,
settingType: 0, // 0未查询 1已设置 2未设置
selectPage: {
type: 0,
dataReady: true
},
searchData: {
frozenType: '', //会员状态
phoneNameCard: "", // 搜索字段
pageName: "wxOpenCarMember",
pageSize: 20,
currentPage: 1,
sortColName: "", // 排序字段
sortType: "", // 排序方式
},
load: false,
total: 0,
memberFields: [], // 顶部标题配置
exportFields: [], // 导出顶部配置
list: [],
multipleList: [],
abnormalSettingSave: 0
};
},
beforeMount() {
this.init();
},
activated(){
this.init();
},
methods: {
init() {
this.load = true;
this.getFindMemberFields(this.searchData.pageName)
.then(list => {
this.memberFields = list;
this.getList();
})
.catch(() => {
this.getList();
});
},
getFindMemberFields(pageName) {
return new Promise((resolve, reject) => {
if (!pageName) {
reject();
}
doFetch(url.findMemberFields, {
pageName
})
.then(res => {
let { errorCode, result } = res.data;
if (errorCode === 0 && Array.isArray(result)) {
resolve(result || []);
} else {
checkFalse(res.data.message);
reject(res);
}
})
.catch(err => {
reject(err);
});
});
},
getList() {
this.load = true;
this.selectPage.dataReady = false;
doFetch(url.getFrozenList, this.searchData)
.then(res => {
this.selectPage.dataReady = true;
this.load = false;
let { errorCode, result } = res.data || {};
if (errorCode === 0) {
let { result: list, totalCount } = result || {};
this.list = list || [];
this.total = totalCount || 0;
}
})
.catch(() => {
this.load = false;
this.selectPage.dataReady = true;
});
},
sortChange(column) {
let { prop, order } = column;
this.searchData.sortColName = prop;
if (order === "descending") {
this.searchData.sortType = "desc";
}
if (order === "ascending") {
this.searchData.sortType = "asc";
}
this.getList();
},
// 搜索选项改变时
onSearchDataChange() {
this.searchData.currentPage = 1;
this.getList();
},
handleCurrentChange(val) {
this.searchData.currentPage = val;
this.getList();
},
handleSizeChange(pageSize){
this.searchData.pageSize = pageSize;
if((this.searchData.currentPage - 1) * this.searchData.pageSize > this.total){
this.searchData.currentPage = 1;
}
this.getList();
},
submitMemberFields(fields) {
doFetch(url.updateFields, {
pageName: this.searchData.pageName,
fields
}).then(res => {
if (res.data.errorCode === 0) {
checkSuccess();
// 需要对选中字段排序
this.init();
} else {
checkFalse(res.data.message);
}
});
},
// 导出选项选择完毕 点击确定
submitExportFields(fields) {
doFetch(url.updateFields, {
pageName: "memberTag",
fields
});
doFetch(url.getEnableDownload).then(res => {
let { errorCode, result } = res.data;
if (errorCode === 0 && result == 1) {
this.exportData.type = 2;
} else {
this.exportData.type = 1;
}
});
let { phoneNameCard, frozenType } = this.searchData || {};
this.exportData.params = {
frozenType,
phoneNameCard,
exportFields: fields
};
this.exportData.dialogVisible = true;
},
freezeConfim(options){
options = options || {};
this.freezeDialogOptions = options;
},
freezeDialogHidden(){
this.freezeDialogOptions = {
visible: false
}
},
// 解冻
unfreezeItem(item) {
doFetchGet(url.updateFrozen,{
status: 0,
pageType: 1,
memberId: item.memberId
}).then(res => {
if (res.data.errorCode === 0) {
this.$message({
message: "解冻成功",
type: "success"
});
this.$nextTick(_ => {
this.getList();
});
}
});
},
// 批量解冻
async clickBatchUnfreeze() {
let { multipleList,list } = this;
if(!(list && list.length)){
this.$message({
message: "暂无数据,无法解冻",
type: "warning"
});
return;
}
if (!(multipleList && multipleList.length)) {
return this.$message.warning("请勾选要修改的会员");
}
this.$confirm("确定解冻?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.batchUnfreeze();
});
},
batchUnfreeze(){
let ids = [];
let { selectPage, multipleList, searchData } = this;
let { phoneNameCard } = searchData;
multipleList.forEach(ele => {
ids.push(ele.memberId);
});
const datas = {
memberIds: ids.join(","), // 会员id
remark: "", // 备注暂留
//所有会员是2 当前是1 其他是0
isCurrent: selectPage.type == 2 ? 2 : 1,
status: 0, // 1 冻结 0 解冻
phoneNameCard,
};
doFetch(url.batchUpdateFrozen, datas).then(async res => {
let {errorCode,result} = res.data;
if (errorCode === 0) {
this.$message({
message: "解冻成功",
type: "success"
});
this.freezeStatusChangeSuccess(result);
}
});
},
freezeStatusChangeSuccess(taskId) {
this.$confirm(
"任务发起成功,请去【企业管理】-【任务中心】查看处理结果和执行进度",
"任务发起成功",
{
confirmButtonText: "去任务中心",
cancelButtonText: "取消",
closeOnClickModal: false,
customClass: "import-link-confirm-content",
type: "warning"
}
)
.then(() => {
this.getList();
window.open(`/gic-web/#/taskDetail/${taskId}`);
})
.catch(() => {
this.getList();
});
},
// 搜索结果导出
download() {
if (this.total) {
this.getFindMemberFields("memberTag").then(list => {
this.exportFields = list;
this.dialogExportFieldVisible = true;
});
} else {
this.$message.warning("无数据");
}
},
linkDetail(memberId) {
this.$router.push({
path: "/customerDetail",
query: { memberId }
});
},
// 去冻结会员页
toFrozenMember(){
this.$router.push({
path: "/frozenList/frozenMember",
});
},
toWhiteList(){
this.$router.push({
path: "/frozenWhiteList",
});
},
}
};
.forzen-container {
padding: 20px;
background-color: #fff;
.search {
margin-bottom: 24px;
.top-header {
display: flex;
align-items: center;
margin-bottom: 20px;
h2 {
margin-right: 20px;
font-size: 16px;
font-weight: bold;
color: #303133;
line-height: 22px;
}
}
.search-item {
display: flex;
justify-content: space-between;
}
}
.basic-info-table {
color: #303133;
font-size: 14px;
.member-name{
width: 140px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.nick-name {
color: #909399;
font-size: 14px;
}
.basic-img {
display: inline-block;
vertical-align: middle;
height: 40px;
width: 40px;
min-width: 40px;
border-radius: 50%;
overflow: hidden;
margin-right: 10px;
}
}
.reason-icon-box{
width: 24px;
height: 24px;
line-height: 24px;
text-align: center;
border-radius: 2px;
&:hover{
background: #E5E6EB;
}
}
.icon-yuanyin{
font-size: 14px;
color: #303030;
}
}
.frozen-table {
.pager {
text-align: right;
padding: 20px 0 0;
}
/deep/.el-table-column--selection {
.cell {
padding-left: 16px;
padding-right: 0px;
}
}
/deep/.cell {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/deep/.table-select-page .cell {
padding-left: 10px;
padding-right: 0;
display: flex;
align-items: center;
.el-dropdown {
padding: 0;
}
}
}
.frozen-list {
height: 100%;
}
.no-setting-page {
height: 100%;
position: relative;
display: flex;
align-items: center;
justify-content: center;
.tips-box {
height: auto;
margin-right: 150px;
.label {
color: #0a1c2b;
font-size: 20px;
font-weight: 500;
}
.el-button--primary {
margin-top: 40px;
border-radius: 2px;
}
}
}
.no-update-page {
position: relative;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
height: 100%;
.icon {
width: 120px;
height: 120px;
margin: 0 auto 16px;
}
.label {
color: #606266;
font-size: 14px;
}
.no-update-alert{
margin-top: 15px;
/deep/.el-icon-info{
color: #2F54EB;
}
}
}
.data-abnormal-page{
position: relative;
text-align: center;
height: calc(100% - 100px);
padding: 0 30px;
.icon{
margin: 116px auto 30px;
color: #FA8C16;
width: 72px;
height: 72px;
font-size: 72px;
}
.title{
color: #303133;
font-size: 14px;
line-height: 20px;
}
.subtitle{
color: #606266;
font-size: 14px;
line-height: 20px;
}
.el-button--primary{
margin: 30px auto;
}
}
.channel-box {
display: inline-block;
+ .channel-box {
margin-left: 6px;
}
.channel-icon {
width: 20px;
height: 20px;
vertical-align: middle;
}
}
.frozen-table /deep/.el-table {
.setting-cell.el-table__cell {
display: flex;
align-items: center;
height: 65px;
padding: 0;
.cell.setting-cell {
display: flex;
justify-content: space-between;
align-items: center;
height: 65px;
padding-right: 0;
.el-icon-setting {
display: flex;
justify-content: center;
align-items: center;
width: 60px;
height: 100%;
font-size: 20px;
color: #666;
cursor: pointer;
background: #eaeaea;
&:hover {
color: #000;
}
}
}
}
/deep/.el-table__empty-block {
z-index: 1;
position: relative;
background: white;
}
}
import { doFetch, doFetchGet } from "@/components/axios/api";
import url from "@/components/axios/url";
import {
checkFalse,
checkSuccess
} from "../../../../../static/js/checkStatus";
import authMethods from "@/mixins/auth";
import defaultImg from "../../../../../static/img/default.png";
import ImportDialog from "@/components/allCustomers/components/importDialog.vue";
import { formatLongTime } from "@/utils/utils";
export default {
name: "frozen-member",
components: {
ImportDialog
},
mixins: [authMethods],
filters: {
formateTime(val){
if (!val || val == -1) return "--";
return formatLongTime(val, 1);
},
},
data() {
return {
dialogImportVisible: false,
defaultImg,
searchData: {
pageSize: 20,
currentPage: 1,
frozenType: -1,
phoneNameCard: '',
wxOrPos: -1,
fieldNames:
"name,nickName,photoUrl,cardNo,crateCardDateString,wxMember,posMember,phoneNumber",
},
total: 0,
list: [],
multipleList: [],
formData:{
frozenType: '',
remark: ''
}
};
},
created() {
this.loadData();
},
activated(){
this.loadData();
},
methods: {
loadData(){
doFetch(url.getMembersForFrozen, this.searchData).then((res) => {
let {errorCode,result} = res.data;
if (errorCode === 0) {
let {result:list,totalCount} = result;
this.list = list || [];
this.total = totalCount || 0;
} else {
checkFalse(res.data.message);
}
});
},
clearSelectedAll(){
this.$refs.multipleTable.clearSelection();
},
delSelectedItem(item){
this.$refs.multipleTable.toggleRowSelection(item,false);
},
handleSizeChange(pageSize){
this.searchData.pageSize = pageSize;
if((this.searchData.currentPage - 1) * this.searchData.pageSize > this.total){
this.searchData.currentPage = 1;
}
this.loadData();
},
handleCurrentChange(currentPage){
this.searchData.currentPage = currentPage;
this.loadData();
},
handleSelectionChange(selectionList){
selectionList = selectionList || [];
this.multipleList = selectionList;
},
resetSelected(){
this.$refs.selectedForm.resetFields();
this.$refs.multipleTable.clearSelection();
},
submitForm() {
this.$confirm("是否冻结所选会员?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
this.frozenMethod();
});
},
frozenMethod() {
let memberIds = this.multipleList.map((item)=>{
return item.memberId
}).join(',')
const datas = {
memberIds: memberIds, // 会员id 多个拼接起来, 隔开
remark: this.formData.remark, // 备注暂留
isCurrent: 0, // 所有会员是2 当前是1 其他是0
status: 1, // 1 冻结 0 解冻
frozenType: this.formData.frozenType, // 101.异常冻结、102.会员主动冻结
};
this.loading = true;
doFetch(url.batchUpdateFrozen, datas)
.then((res) => {
if (res.data.errorCode === 0) {
this.resetSelected();
this.handleToTaskCenter(res.data.result);
} else {
checkFalse(res.data.message);
}
})
.catch((err) => {
this.loading = false;
checkFalse(err);
});
},
handleToTaskCenter(id) {
this.$confirm(
`请去【系统】-【操作任务】-【任务中心】查看处理结果和执行进度`,
`任务发起成功`,
{
type: "success",
confirmButtonText: "去任务中心",
cancelButtonText: "取消",
showClose: false,
}
).then(()=>{
this.loadData();
window.open(`/gic-web/#/taskDetail/${id}`);
}).catch(()=>{
this.loadData();
});
},
batchUpload(){
this.dialogImportVisible = true;
},
linkDetail(memberId) {
this.$router.push({
path: "/customerDetail",
query: { memberId }
});
},
}
};
.container {
// background-color: #fff;
// position: relative;
// box-sizing: border-box;
width: 100%;
display: flex;
height: 100%;
.table-box{
padding: 20px;
border-right: 1px solid #DCDFE6;
.search-box{
margin-bottom: 20px;
}
.basic-info-table {
color: #303133;
font-size: 14px;
.member-name{
width: 140px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.nick-name {
color: #909399;
font-size: 14px;
}
.basic-img {
display: inline-block;
vertical-align: middle;
height: 40px;
width: 40px;
min-width: 40px;
border-radius: 50%;
overflow: hidden;
margin-right: 10px;
}
}
.pager{
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 10px;
}
}
.page-right{
padding: 20px 20px 76px;
box-sizing: border-box;
width: 420px;
position: relative;
max-height: calc(100% - 56px);
.selected-result-box{
height: 522px;
width: 380px;
overflow-y: scroll;
position: relative;
border: 1px solid #DCDFE6;
border-radius: 4px;
.selected-top{
display: flex;
align-items: center;
justify-content: space-between;
position: sticky;
top: 0;
z-index: 1;
width: 100%;
height: 60px;
padding: 0 16px;
.selected-count{
color: #37383A;
font-size: 14px;
}
}
.selected-list .selected-item{
display: flex;
align-items: center;
height: 40px;
&:hover{
background: #F7F8FA;
.icon-close{
background: #E5E6EB;
}
}
.user-header{
width: 30px;
height: 30px;
border-radius: 50%;
margin-left: 17px;
}
.user-name{
color: #47494C;
font-size: 14px;
margin: 0 10px;
flex: 1;
}
.icon-close{
color: #606266;
font-size: 14px;
padding: 3px;
border-radius: 50%;
margin-right: 12px;
}
}
}
}
.selected-bottom-box{
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 56px;
width: 420px;
z-index: 1;
box-shadow: 0px -2px 8px 0px rgba(220,223,230,0.6000);
background: white;
}
}
.frozen-table {
.pager {
text-align: right;
padding: 20px 0 0;
}
/deep/.el-table-column--selection {
.cell {
padding-left: 16px;
padding-right: 0px;
}
}
/deep/.cell {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/deep/.table-select-page .cell {
padding-left: 10px;
padding-right: 0;
display: flex;
align-items: center;
.el-dropdown {
padding: 0;
}
}
}
/deep/.el-table {
/deep/.el-table__empty-block {
z-index: 1;
position: relative;
background: white;
}
}
<template>
<el-contaniner class="container">
<el-main class="table-box">
<el-row
type="flex"
align="middle"
justify="space-between"
class="search-box"
>
<el-input
v-model="searchData.phoneNameCard"
placeholder="输入姓名/昵称/手机号/会员卡号"
style="width: 260px"
@change="loadData"
clearable
>
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
<el-button plain @click="batchUpload"
><i class="iconfont icon-shangchuan"></i> 批量导入</el-button
>
</el-row>
<el-table
:data="list"
tooltip-effect="dark"
ref="multipleTable"
class="member-table"
@selection-change="handleSelectionChange"
v-loading="load"
max-height="700"
>
<el-table-column
type="selection"
min-width="30"
fixed="left"
></el-table-column>
<el-table-column label="会员信息" :min-width="200">
<template slot-scope="{ row }">
<!-- 基本信息 -->
<el-row type="flex" align="middle" class="basic-info-table"
@click.native="linkDetail(row.memberId)">
<img :src="row.thirdImgUrl || defaultImg" class="basic-img" />
<div>
<div class="member-name">
<span v-if="row.memberName">{{ row.memberName }}</span>
<span class="nick-name" v-if="row.nickName"
>({{ row.nickName }})</span
>
</div>
<div v-if="row.phoneNumber">
{{ row.phoneNumber }}
</div>
</div>
</el-row>
</template>
</el-table-column>
<el-table-column label="会员卡号" prop="cardNum" :min-width="160"></el-table-column>
<el-table-column label="开卡时间" :min-width="160">
<template slot-scope="{ row }">
{{ row.crateCardDateString | formateTime }}
</template>
</el-table-column>
</el-table>
<div class="pager" v-if="total > 0">
<dm-pagination
:page-size="searchData.pageSize"
:currentPage="searchData.currentPage"
:page-sizes="[20, 50, 75, 100]"
layout="total,sizes,prev, pager, next"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
:total="total"
>
</dm-pagination>
</div>
<import-dialog
:dialogVisible.sync="dialogImportVisible"
@successImport="(taskId) => handleToTaskCenter(taskId)"
/>
</el-main>
<el-contaniner>
<el-aside width="420px" class="page-right">
<div class="selected-result-box">
<el-row
class="selected-top"
type="flex"
align="middle"
justify="space-between"
>
<div class="selected-count">
已选:{{ multipleList.length }} 位会员
</div>
<el-button type="text" @click="clearSelectedAll">全部清除</el-button>
</el-row>
<div class="selected-list">
<div class="selected-item" v-for="item in multipleList" :key="item">
<img :src="item.thirdImgUrl || defaultImg" class="user-header" />
<div class="user-name">
<span v-if="item.memberName">{{ item.memberName }}</span>
<span class="nick-name" v-if="item.nickName"
>({{ item.nickName }})</span
>
</div>
<div
class="el-icon-close icon-close"
@click="delSelectedItem(item)"
></div>
</div>
</div>
</div>
<el-form
:model="formData"
label-position="top"
label-width="80px"
ref="selectedForm"
>
<el-form-item
label="冻结原因"
prop="frozenType"
required
:rules="{
required: true,
message: '请选择冻结原因',
}"
>
<el-select
v-model="formData.frozenType"
placeholder="请选择冻结原因"
style="width: 380px"
>
<el-option label="异常冻结" :value="1"></el-option>
<el-option label="会员主动冻结" :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item
label="原因备注"
prop="remark"
required
:rules="{
required: true,
message: '请输入原因备注',
}"
>
<el-input
v-model="formData.remark"
:maxlength="20"
show-word-limit
placeholder="请输入原因备注"
></el-input>
</el-form-item>
</el-form>
</el-aside>
<div class="selected-bottom-box">
<el-button plain @click="$router.go(-1)">取消</el-button>
<el-button
type="primary"
@click="submitForm('selectedForm')"
:disabled="
!(
multipleList &&
multipleList.length &&
formData.frozenType &&
formData.remark
)
"
>冻结选中会员</el-button
>
</div>
</el-contaniner>
</el-contaniner>
</template>
<script src="./frozen-member.js"></script>
<!-- <script>
import action from './abnormal-member.js';
export default { ...action };
</script> -->
<style lang="less">
</style>
<style lang="less" scoped src="./frozen-member.less"></style>
import { doFetch, doFetchGet } from "@/components/axios/api";
import url from "@/components/axios/url";
import {
checkFalse,
checkSuccess
} from "../../../../../static/js/checkStatus";
import authMethods from "@/mixins/auth";
import defaultImg from "../../../../../static/img/default.png";
import ImportDialog from "@/components/allCustomers/components/importDialog.vue";
import { formatLongTime } from "@/utils/utils";
import {dateformat} from '@/utils/formatTime';
import FreezeDialog from '../../components/dialog/freeze.vue';
export default {
name: "frozenMemberWhiteList",
components: {
FreezeDialog,
ImportDialog
},
mixins: [authMethods],
filters: {
formatLongTimeDate(val, format) {
if (!val || val == -1) return format == "hms" ? "" : "--";
let str = formatLongTime(val, 1);
let arr = str.split(" ");
if (format == "ymd") {
return arr[0];
} else if (format == "hms") {
return arr[1] || "";
}
return str;
},
formatDate(val, format) {
if (!val || val == -1) return format == "hms" ? "" : "--";
if (format == "ymd") {
return dateformat(new Date(val), 'yyyy-MM-dd');
} else if (format == "hms") {
return dateformat(new Date(val), 'hh:mm:ss');
}
return dateformat(new Date(val), 'yyyy-MM-dd hh:mm:ss');
},
formateTime(val){
if (!val || val == -1) return "--";
return dateformat(val, 'yyyy-MM-dd hh:mm:ss');
},
},
data() {
return {
load: false,
freezeDialogOptions: {
count: 1,
isBatchFreeze: 0,
visible: false
},
whiteImportList:[
{
importType: 17,
importValue: "移入白名单",
}
],
dialogImportVisible: false,
defaultImg,
searchData: {
pageSize: 20,
currentPage: 1,
cardNo: '',
},
total: 0,
list: [],
multipleList: [],
formData:{
frozenType: '',
remark: ''
}
};
},
created() {
this.loadData();
},
activated(){
this.loadData();
},
methods: {
loadData(){
this.load = true;
doFetchGet(url.getFrozenWhiteList, this.searchData).then((res) => {
let {errorCode,result} = res.data;
if (errorCode === 0) {
let {result:list,totalCount} = result;
this.list = list || [];
this.total = totalCount || 0;
} else {
checkFalse(res.data.message);
}
}).finally(()=>this.load = false)
},
handleSizeChange(pageSize){
this.searchData.pageSize = pageSize;
if((this.searchData.currentPage - 1) * this.searchData.pageSize > this.total){
this.searchData.currentPage = 1;
}
this.loadData();
},
handleCurrentChange(currentPage){
this.searchData.currentPage = currentPage;
this.loadData();
},
batchUpload(){
this.dialogImportVisible = true;
},
// 移出白名单
clickRemoveItem(item){
this.load = true;
doFetchGet(url.removeFrozenWhiteMember,{
removeType: 1,
memberId: item.memberId,
}).then(res=>{
if(res.data.errorCode == 0) {
this.$message({
message: "移出成功",
type: "success"
});
this.loadData();
}
}).finally(()=>this.load = false)
},
// 点击删除并冻结
clickRemoveAndFreeze(item){
this.freezeDialogOptions= {
count: 1,
isBatchFreeze: 0,
visible: true,
title: '移出并冻结会员',
submit:async (options)=>{
this.load = true;
doFetchGet(url.removeFrozenWhiteMember,{
removeType: 2,
status: 1,
memberId: item.memberId,
frozenType: options.frozenType,
remark: options.remark || ""
}).then((res)=>{
if (res.data.errorCode === 0){
checkSuccess();
this.freezeDialogOptions = {
visible: false
};
this.loadData();
}
}).finally(()=>this.load = false)
}
}
},
linkDetail(memberId){
this.$router.push({
path: "/customerDetail",
query: { memberId }
});
},
handleToTaskCenter(id) {
this.$confirm(
`请去【系统】-【操作任务】-【任务中心】查看处理结果和执行进度`,
`任务发起成功`,
{
type: "success",
confirmButtonText: "去任务中心",
cancelButtonText: "取消",
showClose: false,
}
).then(()=>{
this.loadData();
window.open(`/gic-web/#/taskDetail/${id}`);
}).catch(()=>{
this.loadData();
});
},
}
};
.container {
padding: 20px;
background: white;
.top-header {
display: flex;
align-items: center;
margin-bottom: 20px;
h2 {
margin-right: 20px;
font-size: 16px;
font-weight: bold;
color: #303133;
line-height: 22px;
}
}
.search-box {
margin-bottom: 20px;
.tips-box{
margin: 0 16px;
i{
color: #2F54EB;
font-size: 14px;
}
span{
color: #606266;
font-size: 14px;
margin-left: 5px;
}
}
}
.member-table {
.basic-info-table {
color: #303133;
font-size: 14px;
.member-name{
width: 140px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.nick-name {
color: #909399;
font-size: 14px;
}
.basic-img {
display: inline-block;
vertical-align: middle;
height: 40px;
width: 40px;
min-width: 40px;
border-radius: 50%;
overflow: hidden;
margin-right: 10px;
}
}
.reason-label {
word-break: break-all;
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
}
/deep/.el-dialog__footer{
border-top: none;
padding: 10px 30px;
}
/deep/.el-dialog__body{
padding: 6px 30px 16px;
}
/deep/.el-dialog__header{
padding: 20px 30px 10px;
}
.pager {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 10px;
}
}
/deep/.el-table {
/deep/.el-table__empty-block {
z-index: 1;
position: relative;
background: white;
}
}
<template>
<div class="container">
<div class="top-header">
<h2>白名单会员 {{ total }}</h2>
</div>
<el-row
type="flex"
align="middle"
justify="space-between"
class="search-box"
>
<el-row type="flex" align="middle">
<el-input
v-model="searchData.cardNo"
placeholder="输入会员卡号"
style="width: 260px"
@change="loadData"
clearable
>
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
<div class="tips-box">
<i class="el-icon-info"></i>
<span>处于白名单列表中的会员不会被冻结(外部接口冻结除外)</span>
</div>
</el-row>
<el-button plain @click="batchUpload"
:limit-code="getCode('memberFrozenWhiteListImport')"
v-if="getCodeAuth('memberFrozenWhiteListImport')"
><i class="icon iconfont icon-shangchuan"></i> 批量导入</el-button
>
</el-row>
<el-table
:data="list"
tooltip-effect="dark"
ref="multipleTable"
class="member-table"
v-loading="load"
>
<el-table-column label="会员信息" :min-width="200">
<template slot-scope="{ row }">
<!-- 基本信息 -->
<el-row
type="flex"
align="middle"
class="basic-info-table"
@click.native="linkDetail(row.memberId)"
>
<img :src="row.thirdImgUrl || defaultImg" class="basic-img" />
<div>
<div class="member-name">
<span v-if="row.memberName">{{ row.memberName }}</span>
<span class="nick-name" v-if="row.nickName"
>({{ row.nickName }})</span
>
</div>
<div v-if="row.phoneNumber">
{{ row.phoneNumber }}
</div>
</div>
</el-row>
</template>
</el-table-column>
<el-table-column label="会员等级" prop="gradeName" :min-width="140"></el-table-column>
<el-table-column label="会员生日" prop="birthdayMD" :min-width="140"></el-table-column>
<el-table-column label="会员卡号" prop="cardNo" :min-width="140"></el-table-column>
<el-table-column label="最近消费时间" :min-width="140">
<template slot-scope="{ row }">
<div>
<div>{{ row.lastCostTime | formatLongTimeDate("ymd") }}</div>
<div>{{ row.lastCostTime | formatLongTimeDate("hms") }}</div>
</div>
</template>
</el-table-column>
<el-table-column
label="移入原因"
prop="remark"
label-class-name="reason-label"
:min-width="140"
></el-table-column>
<el-table-column label="移入时间" :min-width="140">
<template slot-scope="{ row }">
<div>
<div>{{ row.createTime | formatDate("ymd") }}</div>
<div>{{ row.createTime | formatDate("hms") }}</div>
</div>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="220">
<template slot-scope="{ row }">
<!--
:limit-code="getCode('memberIntoCustomDetail')"
v-if="getCodeAuth('memberIntoCustomDetail')"
-->
<el-button type="text" @click="linkDetail(row.memberId)"
>查看</el-button
>
<dm-delete
tips="确认移出白名单吗?"
@confirm="clickRemoveItem(row)"
placement="top-start"
v-if="getCodeAuth('memberFrozenWhiteListRemove')"
>
<el-button type="text"
:limit-code="getCode('memberFrozenWhiteListRemove')">仅移出</el-button>
</dm-delete>
<el-button
type="text"
@click="clickRemoveAndFreeze(row)"
v-if="row.frozenStatus != 1 && getCodeAuth('memberFrozenWhiteListRemoveAndFrozen')"
:limit-code="getCode('memberFrozenWhiteListRemoveAndFrozen')"
>移出并冻结</el-button
>
</template>
</el-table-column>
</el-table>
<div class="pager" v-if="total > 0">
<dm-pagination
:page-size="searchData.pageSize"
:currentPage="searchData.currentPage"
:page-sizes="[20, 50, 75, 100]"
layout="total,sizes,prev, pager, next"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
:total="total"
>
</dm-pagination>
</div>
<import-dialog
:importList="whiteImportList"
:dialogVisible.sync="dialogImportVisible"
@successImport="(taskId) => handleToTaskCenter(taskId)"
/>
<freeze-dialog
v-bind.sync="freezeDialogOptions"
v-if="freezeDialogOptions.visible"
/>
</div>
</template>
<script src="./white-list.js"></script>
<!-- <script>
import action from './abnormal-member.js';
export default { ...action };
</script> -->
<style lang="less">
</style>
<style lang="less" scoped src="./white-list.less"></style>
......@@ -43,6 +43,8 @@ export const constantRouterMap = [
meta: {
title: '全部客户',
keepAlive: true,
componentName: 'allCustomersList',
fromPath: ['/customerDetail'],
},
},
{
......@@ -127,16 +129,29 @@ export const constantRouterMap = [
},
{
path: '/frozenList',
component: _import('wechatmembers', 'frozenList'),
component: _import('wechatmembers', 'frozen-member/frozen-list/frozen-list'),
meta: {
title: '冻结会员列表',
componentName: 'forzenlist',
fromPath: ['/customerDetail','/frozenWhiteList','/frozenList/frozenMember'],
},
},
{
path: '/frozenWhiteList',
component: _import('wechatmembers', 'frozen-member/white-list/white-list'),
meta: {
title: '冻结会员白名单',
componentName: 'frozenMemberWhiteList',
fromPath: ['/customerDetail'],
},
},
{
path: '/frozenList/frozenMember',
component: _import('wechatmembers', 'frozenMember'),
component: _import('wechatmembers', 'frozen-member/frozen-member/frozen-member'),
meta: {
title: '冻结会员',
componentName: 'frozen-member',
fromPath: ['/customerDetail'],
},
},
{
......@@ -319,9 +334,19 @@ export const constantRouterMap = [
{
path: '/abnormal-member',
name: '/abnormal-member',
component: _import('wechatmembers', 'abnormal-member'),
component: _import('wechatmembers', 'abnormal-member/abnormal-member-list/abnormal-member'),
meta: {
title: '异常会员列表',
componentName: 'abnormal-member-list',
fromPath: ['/abnormal-setting','/customerDetail'],
},
},
{
path: '/abnormal-setting',
name: '/abnormal-setting',
component: _import('wechatmembers', 'abnormal-member/abnormal-setting/abnormal-setting'),
meta: {
title: '异常会员配置',
},
},
{
......
<template>
<div class="test-com">
<h1 class="txt">{{msg}}</h1>
</div>
</template>
<script>
export default {
data() {
return {
msg: 'hello'
};
}
};
</script>
<template>
<div>
<div class="filter-list">
<el-select v-model="cardType" style="width: 200px;" placeholder="卡券类型" clearable @change="handleChange">
<el-option
v-for="item in cardOptions"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
<el-input v-model="cardName" style="width: 200px; margin-left: 20px;" clear placeholder="输入卡券名称" @keyup.native.enter="handleSearch"></el-input>
</div>
<el-table
ref="multipleTable"
:data="cardData"
class="card-table"
tooltip-effect="dark"
style="width: 100%"
@row-click="handleCurrentChangeRow"
>
<el-table-column label width="55">
<template slot-scope="scope">
<el-radio class="radio-style" :label="scope.row.integralMallProId" v-model="radio">&nbsp;</el-radio>
</template>
</el-table-column>
<el-table-column label="卡券名称" prop="cardName" >
</el-table-column>
<el-table-column prop="proSubName" label="备注名"></el-table-column>
<el-table-column prop="integralCost" label="兑换限制"></el-table-column>
<el-table-column prop="virtualStock" label="库存"></el-table-column>
</el-table>
<div class="pagination" v-if="total>0">
<dm-pagination
@current-change="cardCurrentChange"
:current-page="cardCurrentPage"
small
:page-size="cardPageSize"
layout="prev, pager, next"
:total="total"
></dm-pagination>
</div>
</div>
</template>
<script>
export default {
name: "card-list",
props: {
pname: String
},
data() {
return {
cardType: null, // 卡券类型
cardOptions: [
{ value: 1, label: '折扣券' },
{ value: 0, label: '抵金券' }
],
cardName: '',
radio: null,
cardCurrentPage: 1,
cardPageSize: 5,
cardLimitType: 1,
cardData: [],
total: 0
};
},
methods: {
// 当某行点击的时候
handleCurrentChangeRow(row) {
this.$nextTick(_ => {
// if (row.integralMallProId == this.radio) {
this.$emit('pass-id', row);
// }
});
},
getCardList() {
var param = {
currentPage: this.cardCurrentPage,
pageSize: this.cardPageSize,
requestProject: this.pname,
cardType: this.cardType,
proName: this.cardName
};
this.axios
.get(this.baseUrl + "/api-integral-mall/page-cards-plug", {params: param})
.then(res => {
const resData = res.data;
if (resData.errorCode == 0) {
if (!!resData.result) {
if (!!resData.result.rows) {
this.cardData = resData.result.rows;
} else {
this.cardData = [];
}
this.total = resData.result.total;
}
} else {
this.$message.error({ duration: 1000, message: resData.message });
}
})
.catch(error => {
this.$message.error({
duration: 10000,
message: error.message
});
});
},
// 当前切换分页
cardCurrentChange(val) {
this.cardCurrentPage = val;
this.getCardList();
},
handleChange(val) {
this.cardCurrentPage = 1;
this.getCardList();
},
handleSearch() {
this.cardCurrentPage = 1;
this.getCardList();
}
},
created() {
const host = window.location.origin;
this.baseUrl = host.indexOf("localhost") > -1 ? 'http://www.gicdev.com' : host;
},
mounted() {
this.getCardList();
}
};
</script>
<style scoped>
.filter-list {
margin-bottom: 10px;
}
.pagination {
text-align: right;
margin-top: 10px;
}
</style>
<template>
<div class="gift-list">
<div class="links-tools-row">
<el-select
v-model="giftVal"
style="width: 200px;"
placeholder="礼品类型"
clearable
@change="handleChange"
>
<el-option
v-for="item in giftOptions"
:key="item.integralMallCategoryId"
:label="item.categoryName"
:value="item.integralMallCategoryId"
></el-option>
</el-select>
<el-input
v-model="giftName"
style="width: 200px; margin-left: 20px;"
placeholder="输入礼品名称"
@keyup.native.enter="handleSearch"
></el-input>
</div>
<div class="goods-link-content">
<ul class="goods-link-list">
<li
class="goods-link-item"
v-for="(item, index) in goodsList"
:key="index"
:class="activeIndex == index ? 'good-highlight' : ''"
@click="selectGoodLinkItem(item, index)"
>
<img
:src="item.mainImageUrl"
class="img"
alt="商品图片"
>
<div class="inline-block goods-message">
<p class="limit-2 pro-name">{{item.proName}}</p>
<p class="gray-color pro-code pro-name">所需积分:{{item.integralCost}}</p>
<p class="pro-price pro-name">所需现金:{{item.cashCost}}</p>
</div>
</li>
</ul>
</div>
<div
class="links-tools-page"
v-if="total > 0"
>
<dm-pagination
@current-change="handleChangePage"
:current-page="currentPage"
:page-size="pageSize"
layout="prev, pager, next"
:total="total"
></dm-pagination>
</div>
</div>
</template>
<script>
import qs from "qs";
export default {
name: "gift-list",
props: {
pname: String
},
data() {
return {
currentPage: 1,
activeIndex: -1,
pageSize: 6,
total: 0,
giftVal: null,
giftOptions: [],
goodsList: [],
giftName: ""
};
},
created() {
const host = window.location.origin;
this.baseUrl = host.indexOf("localhost") > -1 ? 'http://www.gicdev.com' : host;
},
methods: {
// 分类
getGiftList() {
this.axios
.get(
`${
this.baseUrl
}/api-integral-mall/load-category?requestProject=gic-web`
)
.then(res => {
const resData = res.data;
if (resData.errorCode === 0) {
if (!!resData.result && resData.result.length) {
this.giftOptions = resData.result;
}
}
});
},
// 分页换页
handleChangePage(val) {
this.currentPage = val;
this.getData();
},
handleChange() {
this.currentPage = 1;
this.getData();
},
// 分页数据 integralCost积分费用 cashCost现金费用 proName
getData() {
const params = {
requestProject: "gic-web",
currentPage: this.currentPage,
pageSize: this.pageSize,
category: this.giftVal,
giftName: this.giftName,
changeType: -1,
releaseType: -1,
showStatus: 1,
porHot: -1
};
this.axios
.post(
`${this.baseUrl}/api-integral-mall/page-gift`,
qs.stringify(params)
)
.then(res => {
const data = res.data;
if (data.errorCode == 0) {
if (!!data.result && data.result.rows) {
this.goodsList = data.result.rows;
}
this.total = data.result.total || 0;
}
});
},
selectGoodLinkItem(item, i) {
this.activeIndex = i;
this.$emit("pass-gift", item);
},
handleSearch() {
this.currentPage = 1;
this.getData();
}
},
mounted() {
this.getGiftList();
this.getData();
}
};
</script>
<style scoped>
.links-tools-row {
margin-bottom: 10px;
}
.links-tools-row .goods-link-item {
display: inline-block;
vertical-align: middle;
width: 307px;
padding: 8px;
margin-right: 7px;
margin-bottom: 10px;
border: 1px solid #dcdfe6;
border-radius: 4px;
box-sizing: border-box;
}
.links-tools-row .goods-link-item .img {
display: inline-block;
vertical-align: middle;
width: 80px;
height: 80px;
}
.links-tools-page {
text-align: right;
}
.links-tools-row .goods-link-item:hover {
cursor: pointer;
border-color: #2F54EB;
}
.links-tools-row .goods-link-item .goods-message {
margin-left: 10px;
padding: 5px;
width: 198px;
box-sizing: border-box;
font-size: 12px;
}
.links-tools-row .good-highlight {
border-color: #2F54EB;
}
.goods-link-item .goods-message .pro-name {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
margin: 8px 0;
}
</style>
<template>
<div class="plug-sg">
<el-popover
popper-class="plug-sg__popover"
placement="bottom"
trigger="click"
v-model="treeVis"
>
<el-input
slot="reference"
suffix-icon="el-icon-arrow-down"
v-model="ategory"
placeholder="所有分类"
></el-input>
<el-tree
class="plug-sg__tree"
ref="tree"
:default-expand-all="true"
node-key="mallProTagId"
:data="treeData"
:props="defaultProps"
:highlight-current="true"
@node-click="handleNodeClick"
>
</el-tree>
<div class="good-tree-btn">
<el-button
@click.prevent.stop="hideTree"
type="text"
size="small"
style="color:#303133"
>取消</el-button>
<el-button
@click.prevent.stop="confirmBtn"
type="text"
size="small"
>确定</el-button>
</div>
</el-popover>
</div>
</template>
<script>
export default {
name: "goods-tree",
props: {
model: {
type: Object,
default() {
return {};
}
},
width: {
type: String,
default: "300"
},
treeData: Array
},
data() {
return {
treeVis: false,
ategory: "",
defaultProps: {
children: "childrenList",
label: "tagName"
}
};
},
methods: {
handleNodeClick(data) {
this.mallData = data;
this.ategory = data.tagName;
},
hideTree() {
this.treeVis = false;
},
confirmBtn() {
this.treeVis = false;
}
},
watch: {
treeVis(newval) {
if (!newval) {
本身是确保确定之后才传值出去
if (this.mallData) {
this.$emit("pass-mall", this.mallData);
}
}
}
}
};
</script>
<style lang="scss">
.plug-sg {
display: inline-block;
width: 200px;
&__popover {
padding: 10px 0 0 0;
}
&__btn {
height: 36px;
padding: 0 10px;
border-top: 1px solid #ebeef5;
line-height: 34px;
text-align: right;
&--cancel {
color: #606266;
}
}
&__tree {
height: 260px;
width: 200px;
overflow-y: auto;
.el-tree-node__label {
text-overflow: ellipsis;
display: inline-block;
white-space: nowrap;
width: 100%;
overflow: hidden;
}
&::-webkit-scrollbar {
width: 0px;
height: 0px;
}
}
}
.good-tree-btn {
float: right;
margin-right: 10px;
}
</style>
export function hasClass(el, cls) {
if (!el || !cls) return false;
if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
if (el.classList) {
return el.classList.contains(cls);
} else {
return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
}
}
/* istanbul ignore next */
export function addClass(el, cls) {
if (!el) return;
let curClass = el.className;
let classes = (cls || '').split(' ');
for (let i = 0, j = classes.length; i < j; i++) {
let clsName = classes[i];
if (!clsName) continue;
if (el.classList) {
el.classList.add(clsName);
} else if (!hasClass(el, clsName)) {
curClass += ' ' + clsName;
}
}
if (!el.classList) {
el.className = curClass;
}
}
/* istanbul ignore next */
export function removeClass(el, cls) {
let i = 0;
let j;
let clsName;
if (!el || !cls) return;
let classes = cls.split(' ');
let curClass = ' ' + el.className + ' ';
for (i, j = classes.length; i < j; i++) {
clsName = classes[i];
if (!clsName) continue;
if (el.classList) {
el.classList.remove(clsName);
} else if (hasClass(el, clsName)) {
curClass = curClass.replace(' ' + clsName + ' ', ' ');
}
}
if (!el.classList) {
el.className = curClass;
}
}
// 向下查找
function broadcast(componentName, eventName, params) {
this.$children.forEach(child => {
const name = child.$options.name;
if (name === componentName) {
child.$emit.apply(child, [eventName].concat([params]));
} else {
broadcast.apply(child, [componentName, eventName].concat([params]));
}
});
}
// 向上查找组件
export default {
methods: {
dispatch(componentName, eventName, params) {
let parent = this.$parent || this.$root;
let name = parent.$options.name;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.name;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName, params]);
}
},
broadcast(componentName, eventName, params) {
broadcast.call(this, componentName, eventName, params);
},
findComponentUpward(context, componentName) {
let parent = context.$parent;
let name = parent.$options.name;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.name;
}
}
return parent;
}
// findAnyComponent(componentName, root = null) {
// if (!root) {
// root = this.$root;
// }
// const childrens = root.$children;
// let children = null;
// if (childrens.length) {
// for (const child of childrens) {
// const name = child.$options.name;
// if (name === componentName) {
// children = child;
// break;
// } else {
// children = findAnyComponent.call(this, componentName, child);
// if (children) break;
// }
// }
// }
// return children;
// }
}
};
export function debounce(func, wait) {
let timeout;
return function(...args) {
let that = this;
clearTimeout(timeout);
timeout = setTimeout(_ => {
func.apply(that, args);
}, wait);
};
}
export function throttle(func, wait) {
let timeout;
// let previous = 0; // 时间戳
return function(...args) {
let that = this;
if (!timeout) {
timeout = setTimeout(_ => {
timeout = null;
func.apply(that, args);
}, wait);
}
};
}
<template>
<li :class="classes">
<span> {{ data.categoryName }} </span>
<i v-if="showArrow" class="el-icon-arrow-right fr"></i>
</li>
</template>
<script>
export default {
name: 'Casitem',
props: {
data: Object,
tmpItem: Object
},
computed: {
classes() {
return [
`cascader-menu__item`,
{
[`cascader-menu__item--extensible`]: this.data && this.data.isChildren,
[`is-active`]: this.tmpItem.categoryName === this.data.categoryName
}
];
},
showArrow() {
return this.data.isChildren;
}
},
watch: {
tmpItem(val) {
},
data(newval) {
}
}
};
</script>
<style lang="less" scoped>
.cascader-menu__item {
font-size: 14px;
padding: 8px 20px;
width: 152px;
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #606266;
height: 34px;
line-height: 1.5;
box-sizing: border-box;
cursor: pointer;
outline: none;
&:hover {
color: #2F54EB;
}
.fr {
position: absolute;
font-size: 12px;
right: 15px;
top: 50%;
margin-top: -6px;
}
}
.is-active {
color: #2F54EB;
}
</style>
<template>
<div class="caspanel-box">
<span class="ate-keys-box">
<el-input placeholder="搜索关键字" prefix-icon="el-icon-search" v-model="keys" @keyup.native.enter="handleEnter($event)"> </el-input>
</span>
<div class="casitem-box">
<ul v-if="data && data.length" class="casitem-ul">
<Casitem v-for="(item, i) in data" :key="i" :data="item" :tmp-item="tmpItem" @click.native.stop="handleClickItem(item)" @dblclick.native.stop="handleDbClickItem(item)"> </Casitem>
</ul>
</div>
<Caspanel v-if="sublist && sublist.length" :data="sublist" :middle="sublist" :change-on-select="changeOnSelect"></Caspanel>
</div>
</template>
<script>
import Casitem from './casitem';
import Emitter from '../assist/emitter';
import { baseUrl } from '@/config/index.js';
let key = 1;
export default {
name: 'Caspanel',
components: {
Casitem
},
mixins: [Emitter],
props: {
data: {
type: Array,
default() {
return [];
}
},
middle: {
type: Array,
default() {
return [];
}
},
changeOnSelect: Boolean
},
data() {
return {
tmpItem: {},
result: [],
sublist: [],
keys: ''
};
},
methods: {
//
handleEnter(eve) {
let value = eve.target.value;
console.log(this.middle);
this.data = this.middle.filter(el => el.categoryName.indexOf(value) > -1);
},
handleClickItem(item) {
this.tmpItem = Object.assign({}, item);
this.getNextData(item.categoryId);
},
// 双击
handleDbClickItem(item) {
const goodItem = this.findComponentUpward(this, 'gooditem');
item.index = goodItem.goodsIndex;
const tags = Object.assign({}, item);
// 拿到tags比较是否有重复的id
const ategory = this.findComponentUpward(this, 'goods-ategory');
const Tags = ategory.tags;
let hasTag;
if (Tags && Tags.length) {
hasTag = Tags.findIndex(tag => {
return tag.categoryId === item.categoryId;
});
}
if (hasTag != void 0 && hasTag != -1) {
this.$message({
message: '已存在改品类,不能重复添加',
type: 'warning'
});
} else {
this.dispatch('vue-gic-goods-selector', 'handle-ategory', tags);
}
},
// 查找下级
getNextData(id) {
this.axios
.get(`${baseUrl}/api-mall/list-mall-goods-children-category?requestProject=mall&categoryId=${id}`)
.then(res => {
if (res.data.errorCode === 0) {
const data = res.data.result;
if (data && data.length) {
this.sublist = data;
} else if (data && data.length == 0) {
this.sublist = [];
}
} else {
// 占位
}
})
.catch(err => {
console.log(err);
});
},
getKey() {
return key++;
}
}
};
</script>
<style lang="less" scoped>
.caspanel-box {
position: relative;
display: inline-block;
vertical-align: top;
height: 240px;
.ate-keys-box {
display: inline-block;
height: 32px;
width: 138px;
padding: 8px 10px;
border: 1px solid #e8eaec;
border-bottom: none;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
background-color: #f5f7fa;
box-sizing: content-box;
}
.casitem-box {
position: absolute;
border: 1px solid #e8eaec;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
border-top: none;
background-color: #fff;
.casitem-ul {
display: inline-block;
vertical-align: top;
width: 158px;
height: 190px;
list-style: none;
margin: 0;
overflow: auto;
}
}
.caspanel-box {
position: absolute;
margin-left: 12px;
}
}
</style>
<template>
<transition name="transition-drop">
<div class="gic-caspanel">
<Caspanel :data="data" ref="caspanel" :middle="middle" :change-on-select="changeOnSelect"> </Caspanel>
</div>
</transition>
</template>
<script>
import Caspanel from './caspanel';
export default {
name: 'cascader',
components: {
Caspanel
},
props: {
data: {
type: Array,
default() {
return [];
}
},
middle: {
type: Array,
default() {
return [];
}
},
changeOnSelect: {
type: Boolean,
default: false
}
},
data() {
return {
// 占位
};
}
};
</script>
<style lang="less" scoped>
.gic-caspanel {
height: 240px;
padding-bottom: 12px;
overflow-x: auto;
overflow-y: hidden;
font-size: 0px;
}
</style>
import { addClass, removeClass } from './assist/dom';
const Transition = {
beforeEnter(el) {
addClass(el, 'collapse-transition');
if (!el.dataset) el.dataset = {};
el.dataset.oldPaddingTop = el.style.paddingTop;
el.dataset.oldPaddingBottom = el.style.paddingBottom;
el.style.height = '0';
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
},
enter(el) {
el.dataset.oldOverflow = el.style.overflow;
if (el.scrollHeight !== 0) {
el.style.height = el.scrollHeight + 'px';
el.style.paddingTop = el.dataset.oldPaddingTop;
el.style.paddingBottom = el.dataset.oldPaddingBottom;
} else {
el.style.height = '';
el.style.paddingTop = el.dataset.oldPaddingTop;
el.style.paddingBottom = el.dataset.oldPaddingBottom;
}
el.style.overflow = 'hidden';
},
afterEnter(el) {
// for safari: remove class then reset height is necessary
removeClass(el, 'collapse-transition');
el.style.height = '';
el.style.overflow = el.dataset.oldOverflow;
},
beforeLeave(el) {
if (!el.dataset) el.dataset = {};
el.dataset.oldPaddingTop = el.style.paddingTop;
el.dataset.oldPaddingBottom = el.style.paddingBottom;
el.dataset.oldOverflow = el.style.overflow;
el.style.height = el.scrollHeight + 'px';
el.style.overflow = 'hidden';
},
leave(el) {
if (el.scrollHeight !== 0) {
// for safari: add class after set height, or it will jump to zero height suddenly, weired
addClass(el, 'collapse-transition');
el.style.height = 0;
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
}
},
afterLeave(el) {
removeClass(el, 'collapse-transition');
el.style.height = '';
el.style.overflow = el.dataset.oldOverflow;
el.style.paddingTop = el.dataset.oldPaddingTop;
el.style.paddingBottom = el.dataset.oldPaddingBottom;
}
};
export default {
name: 'ElCollapseTransition',
functional: true,
render(h, { children }) {
const data = {
on: Transition
};
return h('transition', data, children);
}
};
<template>
<div class="complex-info">
<div class="complex-items" v-for="(item, i) in abbrInfos" :key="i">
<complex-item :data="item"></complex-item>
<strong class="complex-and" v-if="abbrInfos[i + 1] && abbrInfos[i + 1].length">并且</strong>
</div>
</div>
</template>
<script>
import ComplexItem from './complex-item';
export default {
name: 'complex-info',
props: {
complexData: Array
},
components: {
ComplexItem
},
computed: {
},
watch: {
complexData: {
immediate: true,
handler(newval) {
this.abbrInfos = newval;
}
}
},
data() {
return {
abbrInfos: []
};
}
};
</script>
<style lang="less" scoped>
.complex-items {
.complex-and {
display: inline-block;
margin: 10px 0;
padding: 10px 20px;
color: #64666a;
border-radius: 20px;
background-color: #e2e2e2;
}
}
</style>
<template>
<!-- 缩略信息递归组件的一部分 -->
<div class="complex-item">
<div class="selector-item-txt" v-for="(item, i) in items" :key="i">
<span class="condition" v-if="item.optName"> {{ item.optName }}</span>
<div class="txt">
{{ item.typeName }}{{ item.belong }}
<div class="child-name" v-if="item.childNames.length">
<span v-for="(child, index) in item.childNames" :key="index">
{{ child || '' }}
</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'complex-item',
props: {
data: Array
},
watch: {
data: {
immediate: true,
handler(newval) {
this.items = newval;
}
}
},
data() {
return {
items: []
};
}
};
</script>
<style lang="less" scoped>
.complex-item {
.selector-item-txt {
display: inline-block;
vertical-align: middle;
.txt {
display: inline-block;
padding: 5px 10px;
font-size: 12px;
color: #8d8d8d;
background-color: #f5f7fa;
border-radius: 5px;
.child-name {
display: inline-block;
}
}
.condition {
display: inline-block;
vertical-align: middle;
margin: 0 5px;
font-size: 12px;
}
}
}
</style>
const host = window.location.origin;
export const baseUrl = host.('localhost') ? 'http://www.gicdev.com' : host;
<template>
<li class="good-item">
<el-checkbox v-model="item.check"></el-checkbox>
<div class="good-info">
<img :src="item.img" />
<p>{{ item.name }}</p>
<p>{{ item.code }}</p>
</div>
<span class="price"> {{ item.price }} </span>
<span class="store"> {{ item.store }} </span>
</li>
</template>
<script>
export default {
name: 'GoodFilterItem',
props: {
goods: Object
},
data() {
return {
item: {}
};
},
watch: {
goods: {
immediate: true,
handler(newval) {
this.item = newval;
}
}
}
};
</script>
<template>
<div class="goods-ategory">
<div class="check-title">
<div class="ategory-lists">
<span class="ate-txt" v-if="!tags.length">下方选择品类</span>
<el-tag v-for="tag in tags" size="small" :key="tag.categoryId" closable @close="handleClose(tag)"> {{ tag.categoryName }} </el-tag>
<el-popover placement="top" width="320" trigger="hover" popper-class="selector-popper">
<el-tag class="dm-pop-tag" v-for="tag in tags" size="small" :key="tag.categoryId" closable @close="handleClose(tag)"> {{ tag.categoryName }} </el-tag>
<span class="pop-tips" slot="reference">{{ tags.length }}</span>
</el-popover>
</div>
</div>
<div class="ate-group">
<Cascader :data="ateData" :middle="middle"></Cascader>
</div>
<slot></slot>
</div>
</template>
<script>
import Cascader from './cascader';
import { baseUrl } from '@/config/index.js';
export default {
name: 'goods-ategory',
components: {
Cascader
},
props: {
tags: {
type: Array,
default() {
return [];
}
}
},
data() {
return {
ateData: [{}],
middle: [{}]
};
},
methods: {
getData() {
this.axios
.get(`${baseUrl}/api-mall/list-mall-goods-all-category?requestProject=mall`)
.then(res => {
if (res.data.errorCode == 0) {
const data = res.data.result;
if (data && data.length) {
this.ateData = data;
this.middle = data;
console.log(this.middle);
}
} else {
// 占位
}
})
.catch(err => {
console.log(err);
});
},
handleClose(tag) {
let index = this.tags.findIndex(t => {
return t.categoryId === tag.categoryId;
});
if (index != -1) {
this.tags.splice(index, 1);
}
}
},
created() {
this.getData();
}
};
</script>
<style lang="less" scoped>
.goods-ategory {
.check-title {
height: 48px;
line-height: 48px;
background-color: #ebeef5;
border-top-right-radius: 5px;
border-top-left-radius: 5px;
overflow: hidden;
}
.ategory-lists {
position: relative;
overflow: hidden;
white-space: nowrap;
margin: 7px 20px;
padding: 2px 10px;
height: 32px;
line-height: 24px;
font-size: 0px;
border: 1px solid #dcdfe6;
border-radius: 5px;
box-sizing: border-box;
background-color: #fff;
.ate-txt {
font-size: 14px;
color: #c0c4cc;
}
.pop-tips {
position: absolute;
top: 0;
right: 0;
height: 32px;
line-height: 32px;
width: 70px;
text-align: center;
font-size: 14px;
color: #606266;
background-color: rgba(255, 255, 255, 1);
}
}
.ate-group {
padding: 10px 12px;
overflow-x: scroll;
}
}
.el-tag + .el-tag {
margin-left: 10px;
}
.dm-pop-tag {
margin-right: 10px;
margin-bottom: 5px;
}
.dm-pop-tag + .dm-pop-tag {
margin-left: 0px;
}
</style>
<template>
<div class="goods-brand">
<div class="check-title">
<el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange">全部选择</el-checkbox>
</div>
<el-checkbox-group v-model="checkedBrandes" @change="handleCheckedBrandChange" class="brand-group">
<el-checkbox v-for="brand in brandes" :key="brand.brandId" :label="brand.brandId" class="brand-list">{{ brand.brandName }}</el-checkbox>
</el-checkbox-group>
<slot></slot>
</div>
</template>
<script>
import ElCollapseTransition from './collapse-transition';
import Emitter from './assist/emitter';
import { baseUrl } from '@/config/index.js';
// 品牌
export default {
name: 'goods-brand',
mixins: [Emitter],
components: {
ElCollapseTransition
},
props: {
goodsBrands: Array,
goodsIndex: Array
},
data() {
return {
isIndeterminate: false,
checkAll: false,
checkedBrandes: [],
brandes: []
};
},
methods: {
// 全选
handleCheckAllChange(val) {
this.checkedBrandes = val ? this.brandes : [];
this.isIndeterminate = false;
},
// 复选框多选
handleCheckedBrandChange(val) {
let checkCount = val.length;
this.checkAll = checkCount === this.brandes.length;
this.isIndeterminate = checkCount > 0 && checkCount < this.brandes.length;
},
getBrandList() {
const param = {
currentPage: 1,
pageSize: 20
};
this.axios
.get(`${baseUrl}/api-goods/brandlist?requestProject=goods`, {
params: param
})
.then(res => {
let data;
if (res.data.errorCode === 0) {
data = res.data.result;
this.brandes = data.result;
// 如果超过了20条 totalCount 是总数 根据总数来判断是否还要继续展示第二页的 并不适合全选
} else {
this.$message.error(res.data.message);
}
})
.catch(err => {
this.$message.error(err);
});
}
},
beforeMount() {
// 获取品牌列表
this.getBrandList();
},
watch: {
goodsBrands: {
immediate: true,
handler(newval) {
this.checkedBrandes = newval;
}
},
checkedBrandes(newval) {
this.dispatch('vue-gic-goods-selector', 'pass-checkbox', {
index: this.goodsIndex,
items: newval
});
}
}
};
</script>
<style lang="scss" scoped>
.goods-box {
.check-title {
height: 48px;
line-height: 48px;
padding-left: 20px;
background-color: #ebeef5;
border-top-right-radius: 5px;
border-top-left-radius: 5px;
}
.brand-group {
padding: 10px 20px;
}
.brand-list {
line-height: 30px;
margin-left: 0;
margin-right: 10px;
}
}
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
</style>
<template>
<div class="collection">
<div class="coll-options" v-if="goodIndex[1] <= 3">
<el-radio-group v-model="bindCondition" @change="handleChange">
<el-radio :disabled="disabled" :label="0">并且</el-radio>
<el-radio :disabled="disabled" :label="1">或者</el-radio>
<el-radio :disabled="disabled" :label="2">剔除</el-radio>
</el-radio-group>
</div>
<div class="coll-options" v-else></div>
<div class="oper-btn">
<el-popover placement="top" width="150" trigger="click" v-model="visiable">
<el-radio-group v-model="goodOption" class="pop-options">
<el-radio :label="1" class="pop-goodlist">商品品牌</el-radio>
<el-radio :label="2" class="pop-goodlist">商品品类</el-radio>
<el-radio :label="3" class="pop-goodlist">商品规格</el-radio>
<el-radio :label="4" class="pop-goodlist">商品属性</el-radio>
<el-radio :label="5" class="pop-goodlist">部分商品</el-radio>
</el-radio-group>
<div class="insert-dir">
<el-button type="text" @click="insertUpward">上方插入</el-button>
<el-button type="text" @click="insertUpdown">下方插入</el-button>
</div>
<el-button type="text" class="button-txt" slot="reference" :disabled="length == 5">插入条件</el-button>
</el-popover>
<span class="cut-line">|</span>
<el-button type="text" class="button-txt" @click="deleteCondition">删除</el-button>
</div>
</div>
</template>
<script>
import Emitter from './assist/emitter';
// 集合条件 并且 或者 剔除
export default {
name: 'goods-collection',
props: {
disabled: Boolean,
goodIndex: Array,
length: Number,
condition: Number
},
mixins: [Emitter],
data() {
return {
bindCondition: '',
goodOption: 1, // 插入的商品
visiable: false
};
},
methods: {
deleteCondition() {
// 触发selector 组件去删除
this.dispatch('vue-gic-goods-selector', 'delete-gooditem', this.goodIndex);
},
handleChange(val) {
this.dispatch('vue-gic-goods-selector', 'pass-radioGroup', { index: this.goodIndex, val: this.bindCondition });
},
insertUpward() {
this.dispatch('vue-gic-goods-selector', 'insert-uselector', { index: this.goodIndex, type: parseInt(this.goodOption, 10) });
this.visiable = false;
},
insertUpdown() {
this.dispatch('vue-gic-goods-selector', 'insert-dselector', { index: this.goodIndex, type: parseInt(this.goodOption, 10) });
this.visiable = false;
}
},
watch: {
condition: {
immediate: true,
handler(val) {
this.bindCondition = val;
}
}
}
};
</script>
<style lang="less" scoped>
.collection {
margin-left: 20px;
margin-right: 20px;
padding-top: 10px;
border-top: 1px dashed #dcdfe6;
display: flex;
justify-content: space-between;
.coll-options {
display: inline-block;
}
.oper-btn {
display: inline-block;
text-align: right;
.cut-line {
color: #dcdfe6;
}
}
}
.pop-options {
text-align: center;
.pop-goodlist {
margin-left: 0;
line-height: 26px;
}
}
.insert-dir {
border-top: 1px solid #dcdfe6;
padding-top: 10px;
text-align: center;
}
.button-txt {
color: #a4a7aa;
&:hover {
color: #2F54EB;
}
}
</style>
import GoodsBrand from './goods-brand'; // 商品品牌
import GoodsAtegory from './goods-ategory'; // 商品品类
import GoodsSpecifications from './goods-specifications'; // 规格
import GoodsSome from './goods-some'; // 部分商品
import GoodsProperties from './goods-properties'; // 商品属性
import ElCollapseTransition from './collapse-transition';
export default {
name: 'gooditem',
components: {
GoodsBrand,
GoodsAtegory,
GoodsSpecifications,
GoodsSome,
GoodsProperties,
ElCollapseTransition
},
props: {
type: {
type: String,
validator: val => ['brand', 'ategory', 'specifications', 'properties', 'some'].indexOf(val) > -1
},
goodsIndex: Array,
tags: Array,
goodsBrands: Array
},
data() {
return {};
},
render(h) {
// 定义五种类型
const slotVal = this.$slots.default;
const type = this.type;
let FinalComponent;
// 扩展的属性
const goodsData = {
props: {
tags: this.tags,
'goods-brands': this.goodsBrands,
'goods-index': this.goodsIndex
}
};
if (type == 'brand') {
// 品牌
FinalComponent = <goods-brand {...goodsData}>{slotVal}</goods-brand>;
} else if (type == 'ategory') {
// 品类
FinalComponent = <goods-ategory {...goodsData}>{slotVal}</goods-ategory>;
} else if (type == 'specifications') {
// 规格
FinalComponent = <goods-specifications {...goodsData}>{slotVal}</goods-specifications>;
} else if (type == 'properties') {
// 属性
FinalComponent = <goods-properties {...goodsData}>{slotVal}</goods-properties>;
} else if (type == 'some') {
// 部分商品
FinalComponent = <goods-some {...goodsData}>{slotVal}</goods-some>;
}
return <el-collapse-transition>{FinalComponent}</el-collapse-transition>;
}
};
<template>
<div class="goods-properties">
<div class="check-title">
选择属性
<load-select v-model="propVal" @scrollload="loadMore" :load="load" @change-load="changeLoad">
<gic-load-item v-for="item in propOptions" :key="item.propertyId" :value="item.propertyId" :label="item.propertyName" @pass-item="resiver"> </gic-load-item>
</load-select>
<!-- 多选有 包含其一 和 包含所有-->
<!-- <el-select v-model="exclude" placeholder="请选择" style="width: 120px" v-if="propType === 'TYP_CHECK'">
<el-option
v-for="item in excludeOption"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select> -->
</div>
<div class="prop-content">
<!-- 单选 属性勾选 TYP_SINGLE -->
<!-- v-if="propType === 'TYP_SINGLE' || propType === 'TYP_CHECK'" -->
<div v-if="propType === 'TYP_SINGLE' || propType === 'TYP_CHECK'" class="prop-type">
<el-checkbox :indeterminate="isIndeterminate" v-model="propList.checkAll" @change="handleCheckAllChange">全选</el-checkbox>
<div class="box-group">
<el-checkbox-group v-model="checkedSpes" @change="handleCheckedSpesChange">
<el-checkbox v-for="spe in spes" class="dm-checkbox" :label="spe.propertyId" :key="spe.propertyId">
{{ spe.valueName }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<!-- 文本 关键字搜索 -->
<!-- <div v-if="propType === 'TYP_TEXT'" class="prop-type">关键字搜索:<el-input v-model="propList.text" style="width: 200px;" maxlength="8" placeholder="关键字" clearable></el-input></div> -->
<!-- 整数 没有小数-->
<!-- <div v-if="propType === 'TYP_NUM'" class="prop-type">
<el-select v-model="interval" placeholder="请选择" style="width: 100px">
<el-option v-for="item in intervalOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
<el-input placeholder="请输入" maxlength="8" minlength="1" style="width: 150px" v-model="propList.number"></el-input>
</div> -->
<!-- 实数 有小数-->
<!-- <div v-if="propType === 'TYP_REAL_NUM'" class="prop-type">
<el-select v-model="interval" placeholder="请选择" style="width: 100px;">
<el-option v-for="item in intervalOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
<el-input placeholder="请输入" maxlength="8" minlength="1" style="width: 150px;" v-model="propList.rel_num"></el-input>
</div> -->
<!-- 时间 TYP_TIME -->
<!-- <div v-if="propType === 'TYP_TIME'" class="prop-type">
<el-date-picker v-model="propList.Time" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间"> </el-date-picker>
</div> -->
</div>
<slot></slot>
</div>
</template>
<script>
import LoadSelect from './load-select';
import GicLoadItem from './load-item';
import Emitter from './assist/emitter';
import { baseUrl } from '@/config/index.js';
// 文本 单选 多选 勾选 整数 实数 货币 时间 百分比
const PROP_TYPE = [
'TYP_TEXT', // 文本
'TYP_SINGLE', // 单选
'TYP_CHECK', // 多选
'TYP_MULTI', // 勾选
'TYP_NUM', // 整数
'TYP_REAL_NUM', // 实数
'TYP_CURRENCY', // 货币
'TYP_TIME', // 时间
'TYP_PERCENT' // 百分比
];
export default {
name: 'goods-properties',
mixins: [Emitter],
props: {
goodsIndex: Array
},
components: {
GicLoadItem,
LoadSelect
},
data() {
return {
propOptions: [],
propVal: [],
checkedSpes: [],
load: false,
currentPage: 1,
spes: [],
interval: null,
isIndeterminate: false,
propType: '',
propList: {
Time: '',
checkAll: false,
number: null,
rel_num: null
}
// intervalOption: [{ label: '>=', value: 0 }, { label: '<=', value: 1 }, { label: '=', value: 2 }]
};
},
watch: {
checkedSpes(newval) {
this.dispatch('vue-gic-goods-selector', 'pass-property', {
index: this.goodsIndex,
items: {
propId: newval,
parentId: this.propertyId,
condition: this.exclude
}
});
},
},
methods: {
loadMore() {
if (this.propOptions.length == (this.currentPage - 1) * 20) {
this.getPropData('success');
} else {
this.load = true;
}
},
changeLoad() {
this.load = false;
},
handleCheckAllChange(val) {
this.checkedSpes = val ? this.spes : [];
this.isIndeterminate = false;
},
handleCheckedSpesChange(value) {
let checkedCount = value.length;
this.propList.checkAll = checkedCount === this.spes.length;
this.isIndeterminate = checkedCount > 0 && checkedCount < this.spes.length;
},
// 商品属性列表下拉数据
getPropData(suc) {
const param = {
currentPage: this.currentPage,
pageSize: 20
};
this.axios
.get(`${baseUrl}/api-goods/page-property?requestProject=goods`, {
params: param
})
.then(res => {
if (res.data.errorCode === 0) {
let data = res.data.result;
this.propOptions = this.propOptions.concat(data.result);
this.currentPage++;
if (suc == 'success') {
this.load = true;
}
} else {
// this.$message.error(res.data.message);
}
})
.catch(err => {
console.log(err);
// this.$message.error(error);
});
},
resiver(val) {
this.propVal = val;
const item = this.propOptions.find(el => el.propertyName === val);
this.propertyId = item.propertyId;
// propertyType 字段属性类型
const type = PROP_TYPE.find(type => type === item.propertyType);
// 属性类型
if (type) {
this.propType = type;
if (this.propType == 'TYP_SINGLE' || this.propType == 'TYP_CHECK') {
const param = {
currentPage: 1,
pageSize: 20,
propertyId: item.propertyId
};
this.axios
.get(`${baseUrl}/api-goods/page-property-value?requestProject=goods`, {
params: param
})
.then(res => {
if (res.data.errorCode == 0) {
const data = res.data.result;
if (data.result && data.result.length) {
this.spes = data.result;
} else {
this.spes = [];
}
}
})
.catch(err => {
console.log(err);
});
}
// 多选
}
}
},
beforeMount() {
this.getPropData();
},
mounted() {
this.$on('pass-item', this.resiver);
}
};
</script>
<style lang="less" scoped>
.goods-properties {
.check-title {
height: 48px;
line-height: 48px;
padding-left: 20px;
background-color: #ebeef5;
font-size: 14px;
color: #606266;
border-top-right-radius: 5px;
border-top-left-radius: 5px;
}
.prop-content {
padding: 10px 20px;
.box-group {
margin-top: 10px;
padding-top: 10px;
border-top: 1px dashed #dcdfe6;
}
.prop-type {
color: #606266;
font-size: 14px;
}
}
}
</style>
<template>
<div class="goods-specifications">
<div class="check-title">
选择规格 &nbsp;
<load-select v-model="specvalue" @scrollload="loadMore" :load="load" @change-load="changeLoad">
<gic-load-item v-for="item in speOptions" :key="item.standardId" :value="item.standardId" :label="item.standardName" @pass-item="resiver"> </gic-load-item>
</load-select>
<!-- 规格类型筛选 -->
<div class="spe-type">
<el-radio v-model="speRadio" label="2">通过规格值筛选</el-radio>
<el-radio v-model="speRadio" label="1">通过规格值组筛选</el-radio>
</div>
</div>
<div class="spe-con">
<spes v-if="spesGroup" :list-spes="spesList" :goods-index="goodsIndex"></spes>
<spe-group v-if="spesVal" :list-group="groupList" :goods-index="goodsIndex" @spe-list="speList"></spe-group>
<!-- <spes v-if="spesGroup && speRadio == '1'" :list-spes="spesList" :goods-index="goodsIndex"></spes>
<spe-group v-if="spesVal && speRadio == '2'" :list-group="groupList" :goods-index="goodsIndex"></spe-group> -->
</div>
<slot></slot>
</div>
</template>
<script>
import Emitter from './assist/emitter';
import Spes from './spes';
import SpeGroup from './spe-group';
import LoadSelect from './load-select';
import GicLoadItem from './load-item';
import { baseUrl } from '@/config/index.js';
export default {
name: 'goods-specifications',
mixins: [Emitter],
components: {
Spes,
SpeGroup,
LoadSelect,
GicLoadItem
},
props: {
goodsIndex: Array
},
data() {
return {
spesGroup: false,
spesVal: false,
speOptions: [], // 规格
speRadio: '2',
currentPage: 1,
load: false,
specvalue: '',
spesList: [],
groupList: [] // 规格值 在规格值组里面
};
},
methods: {
speList(list) {
this.dispatch('vue-gic-goods-selector', 'pass-spe-group-list', {
index: this.goodsIndex,
item: list
});
},
loadMore() {
if (this.speOptions.length == (this.currentPage - 1) * 20) {
this.getOptionsData('success');
} else {
this.load = true;
}
},
// 规格查找
getOptionsData(suc) {
const param = {
type: 'TYP_NORMAL',
currentPage: this.currentPage,
pageSize: 20
};
this.axios
.get(`${baseUrl}/api-goods/page-standard?requestProject=goods`, {
params: param
})
.then(res => {
let data;
if (res.data.errorCode === 0) {
data = res.data.result;
this.speOptions = this.speOptions.concat(data.result);
this.currentPage++;
if (suc == 'success') {
this.load = true;
}
} else {
this.$message.error(res.data.message);
}
})
.catch(err => {
this.$message.error(err);
});
},
changeLoad() {
this.load = false;
},
resiver(val) {
// 选中的值
console.log(val);
this.specvalue = val;
this.specList = this.speOptions.find(val => val.standardName == this.specvalue);
if (this.specList && this.specList.standardId) {
this.validateGoods();
}
},
// 查询是规格值还是规格值组
validateGoods() {
this.axios.get(`${baseUrl}/api-goods/validate-standard-pull-data?requestProject=goods&standardId=${this.specList.standardId}`).then(res => {
const data = res.data;
// false是规格值组 true 是规格值
if (data.errorCode === 0) {
if (data.result) {
this.spesGroup = false;
this.spesVal = true;
// this.speRadio = '2';
this.getSpeData();
} else {
this.spesVal = false;
this.spesGroup = true;
// this.speRadio = '1';
this.getSpeGroupData();
}
}
});
},
// 规格值分组数据
getSpeGroupData() {
const param = {
currentPage: 1,
pageSize: 20,
parentId: this.specList.standardId,
type: 'TYP_GROUP',
categoryId: this.specList.gicStandardId
};
this.axios
.get(`${baseUrl}/api-goods/page-standard?requestProject=goods`, {
params: param
})
.then(res => {
if (res.data.errorCode === 0) {
const data = res.data.result;
if (data.result && data.result.length) {
this.spesList = data.result;
} else {
this.spesList = [];
}
}
})
.catch(err => {
console.log(err);
});
},
// 规格值数据
getSpeData() {
const param = {
currentPage: 1,
pageSize: 20,
standardId: this.specList.standardId
};
this.axios
.get(`${baseUrl}/api-goods/page-standard-values?requestProject=goods`, {
params: param
})
.then(res => {
if (res.data.errorCode === 0) {
const data = res.data.result;
if (data.result && data.result.length) {
const middleData = data.result;
this.groupList = middleData;
// this.groupList = middleData.map(el => {
// if (el.standardGroupNames && el.standardGroupNames.length) {
// el.standardGroup = [];
// for (let i = 0; i < el.standardGroupNames.length; i++) {
// el.standardGroup.push({
// valueName: el.standardGroupNames[i],
// valueId: el.standardGroupIds[i]
// });
// }
// }
// return el;
// });
} else {
this.groupList = [];
}
}
})
.catch(err => {
console.log(err);
});
}
},
mounted() {
// this.$on('pass-item', this.resiver);
},
beforeMount() {
this.getOptionsData();
}
};
</script>
<style lang="less" scoped>
.goods-box {
.check-title {
height: 48px;
line-height: 48px;
padding-left: 20px;
font-size: 14px;
background-color: #ebeef5;
border-top-right-radius: 5px;
border-top-left-radius: 5px;
}
.spe-con {
max-height: 285px;
padding: 10px 20px;
overflow: hidden;
}
.spe-type {
float: right;
margin-right: 20px;
}
}
</style>
import vueGicSelector from './index.vue';
const gicSelector = {
install(Vue, options) {
Vue.component(vueGicSelector.name, vueGicSelector);
}
};
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(gicSelector);
}
export default gicSelector;
<template>
<li class="load-item" @click="handleClick">
<slot>
<span>{{ currentLabel }}</span>
</slot>
</li>
</template>
<script>
import Emitter from './assist/emitter';
export default {
name: 'gic-load-item',
mixins: [Emitter],
props: {
value: {
required: true
},
label: [String, Number]
},
computed: {
currentLabel() {
return this.label || this.value;
}
},
methods: {
handleClick() {
this.$emit('pass-item', this.currentLabel);
}
}
};
</script>
<style lang="less" scoped>
.load-item {
height: 24px;
line-height: 24px;
padding: 4px 10px;
cursor: pointer;
color: #606266;
&:hover {
background-color: #f5f7fa;
}
}
</style>
<template>
<el-popover placement="bottom" width="195" trigger="click" v-model="loadShow" popper-class="select-popper">
<ul class="load-ul" @scroll="handleScroll" ref="loadbox">
<slot></slot>
<p class="el-select-dropdown__empty no-data" v-if="isEmpty">
<i class="el-icon-loading"></i>
{{ emptyTxt }}
</p>
</ul>
<div class="load-select" slot="reference">
<el-input placeholder="请选择" v-model="selectLabel" readonly="readonly" :class="{ 'is-focus': visible }" ref="select">
<i slot="suffix" :class="['el-select__caret', 'el-input__icon', 'el-icon-arrow-up' + iconClass]" @click="handleIconClick"> </i>
</el-input>
</div>
</el-popover>
</template>
<script>
import LoadItem from './load-item'; // 加载的item选项
export default {
name: 'load-select',
components: {
LoadItem
},
props: {
value: {
required: true
},
load: {
type: Boolean,
default: false
},
loadover: {
type: Boolean,
default: false
}
},
computed: {
iconClass() {
if (this.visible) {
return ' is-reverse';
} else {
return '';
}
}
},
data() {
return {
readonly: false,
visible: false,
selectLabel: '',
isEmpty: false,
emptyTxt: '',
loadShow: false
};
},
watch: {
load: {
immediate: true,
handler(newval) {
if (newval) {
this.isEmpty = false;
this.$emit('change-load');
// 方案一 加载时候移动一下
// 方案二 价格loadover 标识
this.$refs.loadbox.scrollTop = this.$refs.loadbox.scrollHeight - 250;
}
}
},
value(newval) {
this.selectLabel = newval;
this.loadShow = false;
this.visible = false;
},
loadShow(newval) {
if (newval) {
this.visible = true;
} else {
this.visible = false;
}
}
},
methods: {
handleIconClick() {
this.visible = !this.visible;
},
// 滚动监听
handleScroll(e) {
const ele = e.target;
// scrollHeight 获取元素内容高度
// scrollTop 可以获取或者设置元素的偏移值
// 满足滚到底部和上次加载结束两个条件
if (ele.scrollHeight - ele.scrollTop == 220 && !this.isEmpty) {
// 滚到底部 先加载
this.emptyTxt = '加载中';
this.isEmpty = true;
setTimeout(_ => {
this.$emit('scrollload');
}, 500);
}
}
}
};
</script>
<style lang="less" scoped>
.load-select {
display: inline-block;
width: 200px;
cursor: pointer;
}
.load-ul {
position: relative;
max-height: 200px;
padding-bottom: 20px;
overflow: auto;
.no-data {
padding: 0;
text-align: center;
color: #ccc;
}
}
.el-select__caret {
color: #c0c4cc;
font-size: 14px;
transition: transform 0.3s;
transform: rotateZ(180deg);
cursor: pointer;
}
.el-select__caret.is-reverse {
transform: rotateZ(0deg);
}
</style>
<template>
<div class="sku-filter">
<div class="title">
<el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange" v-if="!skuFlt"></el-checkbox>
<span class="arrow-bottom" v-if="skuFlt"></span>
<span class="good-info">
商品信息
</span>
<el-popover placement="bottom" width="280" trigger="click" v-model="showPopPrice">
<el-input placeholder="最低单价" :minlength="1" :maxlength="6" style="width: 90px;" v-model="lowPrice"></el-input>
<el-input placeholder="最高单价" :minlength="1" :maxlength="6" style="width: 90px;" v-model="mostPrice"></el-input>
&nbsp;
<el-button type="primary" @click="searchGoods">搜索</el-button>
<span class="price" :class="{ 'showpop-price': showPopPrice }" slot="reference" @click="showPopPrice = !showPopPrice">
单价
<i class="icon iconfont icon-shaixuan-shi icon-filter"></i>
</span>
</el-popover>
<el-popover placement="bottom" width="280" trigger="click" v-model="showPopNum">
<el-input placeholder="最低数量" :minlength="1" :maxlength="6" style="width: 90px;" v-model="lowStore"></el-input>
<el-input placeholder="最高数量" :minlength="1" :maxlength="6" style="width: 90px;" v-model="mostStore"></el-input>
&nbsp;
<el-button type="primary" @click="storeSearch">搜索</el-button>
<span class="price store" :class="{ 'showpop-num': showPopNum }" slot="reference" @click="showPopNum = !showPopNum">
库存
<i class="icon iconfont icon-shaixuan-shi icon-filter"></i>
</span>
</el-popover>
</div>
<ul class="goods-sku-lists">
<sku-left-item v-for="(item, i) in items" :key="i" :goods="item" :inx="i" :check="skuFlt" :checkbox.sync="item.check" @changeIndeterminate="handleIndeter"></sku-left-item>
<span class="el-table__empty-text no-data" v-if="!items || !items.length">暂无数据</span>
</ul>
</div>
</template>
<script>
import SkuLeftItem from './sku-left-item';
import { throttle } from './assist/util';
// 正则
const INTEGER_REG = /^[1-9]{1}[0-9]{0,5}$/; // 最小1 最大999999 的整数
export default {
name: 'SkuFilterTable',
components: {
SkuLeftItem
},
props: {
data: Array,
skufilter: Boolean // sku筛选
},
data() {
return {
skuFlt: null,
checkAll: false,
visiable: false,
items: [],
mostPrice: null,
lowPrice: null,
lowStore: null,
mostStore: null,
showPopPrice: false,
showPopNum: false,
isIndeterminate: false,
checkedGoods: [] // 选中的商品
};
},
methods: {
// 处理最低的库存
handleLowStore() {
// 占位
},
// 处理最高的库存
handleMostStore() {
// 占位
},
// item 是勾选的sku信息
resiverSku(val) {
// val里面有个check
if (val.checkAll !== void 0) {
this.$emit('resiverAllSku', {
skus: val.skus,
inx: val.inx,
})
} else {
this.$emit('resiverSku');
}
},
// 单价搜索
searchGoods() {
// 占位
},
// 库存搜索
storeSearch() {
// 占位
},
handleCheckAllChange(val) {
this.items = this.items.map(el => ({
...el,
check: val
}));
this.isIndeterminate = false;
this.$emit('resiverSku', this.items);
},
// 修改checkbox的状态
handleIndeter() {
this.checkedGoods = this.items.filter(el => el.check);
this.checkAll = this.checkedGoods.length === this.items.length;
this.isIndeterminate = this.checkedGoods.length > 0 && this.checkedGoods.length < this.items.length;
},
// 正则限制输入价格和库存
regInput(val) {
if (!INTEGER_REG.test(val)) {
this.$message.warning('单价必须大于0!');
}
}
},
mounted() {
this.$on('passku', this.resiverSku);
// 低价
this.$watch(
'lowPrice',
throttle(newval => {
this.regInput(newval);
}, 500)
);
// 高级
this.$watch(
'mostPrice',
throttle(newval => {
this.regInput(newval);
}, 500)
);
},
watch: {
skufilter: {
immediate: true,
handler(newval) {
this.skuFlt = newval;
}
},
data: {
immediate: true,
handler(newval) {
this.items = newval;
}
},
checkedGoods: {
immediate: true,
handler(newval) {
this.$emit('resiverSku');
}
}
}
};
</script>
<style lang="less" scoped>
.sku-filter {
height: 350px;
padding-bottom: 20px;
.title {
height: 44px;
line-height: 44px;
padding: 0 10px;
font-size: 14px;
background-color: #f5f7fa;
border-top-right-radius: 5px;
border-top-left-radius: 5px;
color: #909399;
.arrow-bottom {
display: inline-block;
vertical-align: middle;
width: 14px;
}
span {
display: inline-block;
text-align: center;
height: 44px;
}
.good-info {
width: 160px;
text-align: left;
padding-left: 10px;
}
.price {
position: relative;
width: 60px;
outline: none;
border: none;
.icon-filter::before {
top: 10px;
}
}
.store {
position: relative;
width: 60px;
}
.showpop-price,
.showpop-num {
background-color: #e3e5eb;
}
}
.goods-sku-lists {
height: 303px;
overflow: auto;
list-style: none;
.no-data {
display: inline-block;
width: 100%;
text-align: center;
font-size: 14px;
}
}
}
</style>
<template>
<li class="sku-item" v-if="popShow">
<el-popover placement="bottom" :width="320" trigger="hover">
<div slot="reference">
<el-checkbox v-model="skuItem.check" @change="handleChange"></el-checkbox>
<div class="good-img">
<img :src="skuItem.img" />
</div>
<div class="good-info">
<p class="name" :title="skuItem.goodsName">{{ skuItem.goodsName }}</p>
<p class="code" :title="skuItem.goodsCode">{{ skuItem.goodsCode }}</p>
</div>
</div>
<table-checkbox :data="skuItem.skus" b-color="white"></table-checkbox>
</el-popover>
</li>
<li class="sku-item" v-else>
<el-checkbox v-model="skuItem.check" @change="handleChange"></el-checkbox>
<div class="good-img">
<img :src="skuItem.img" />
</div>
<div class="good-info">
<p class="name">{{ skuItem.goodsName }}</p>
<p class="code">{{ skuItem.goodsCode }}</p>
</div>
</li>
</template>
<script>
import TableCheckbox from './table-checkbox';
export default {
name: 'sku-item',
components: {
TableCheckbox
},
props: {
skuList: Object,
popShow: Boolean
},
data() {
return {
skuItem: {},
visiable: false
};
},
methods: {
handleChange(val) {
this.$emit('update:check', val);
this.$emit('pass-check');
}
},
watch: {
skuList: {
immediate: true,
handler(newval) {
this.skuItem = newval;
}
}
}
};
</script>
<style lang="less" scoped>
.sku-item {
height: 56px;
line-height: 56px;
padding: 0 10px;
box-sizing: border-box;
border-bottom: 1px solid #dcdfe6;
.good-img {
margin: 0 10px;
display: inline-block;
vertical-align: middle;
height: 35px;
width: 35px;
img {
height: 100%;
width: 100%;
}
}
.good-info {
display: inline-block;
vertical-align: middle;
p {
margin: 0;
font-size: 12px;
width: 73px;
height: 20px;
line-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
&:last-child {
border-bottom: none;
}
}
</style>
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment