manager 升级到vue3

This commit is contained in:
pikachu1995@126.com
2026-05-25 10:49:09 +08:00
parent e7350899bf
commit 615ee91511
239 changed files with 22907 additions and 30841 deletions

View File

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

View File

@@ -122,7 +122,7 @@ export default {
deactivated() {
this.destroyTinymce();
},
destroyed() {
unmounted() {
this.destroyTinymce();
},
};

View File

@@ -1,4 +1,4 @@
const plugins = [
'advlist', 'anchor', 'autolink', 'autosave', 'code', 'codesample', 'directionality', 'emoticons', 'fullscreen', 'image', 'importcss', 'insertdatetime', 'link', 'lists', 'media', 'nonbreaking', 'pagebreak', 'preview', 'save', 'searchreplace', 'table', 'template', 'visualblocks', 'visualchars', 'wordcount'
'advlist', 'anchor', 'autolink', 'autosave', 'code', 'codesample', 'directionality', 'emoticons', 'fullscreen', 'image', 'importcss', 'insertdatetime', 'link', 'lists', 'media', 'nonbreaking', 'pagebreak', 'preview', 'save', 'searchreplace', 'table', 'visualblocks', 'visualchars', 'wordcount'
]
export default plugins

View File

@@ -1,70 +1,90 @@
<template>
<div class="wrapper">
<Button @click="handleClickUploadImage">上传图片</Button>
<Modal v-model="show" width="850" @on-ok="callback" title="上传图片">
<div class="import-oss" @click="importOSS">
从资源库中导入
</div>
<el-button @click="handleClickUploadImage">上传图片</el-button>
<el-dialog v-model="show" width="850px" title="上传图片" append-to-body :z-index="3500">
<div class="import-oss" @click="importOSS">从资源库中导入</div>
<div style="display: flex; flex-wrap: wrap">
<vuedraggable
:animation="200"
:list="images"
>
<vuedraggable :animation="200" :list="images">
<div
v-for="(item, __index) in images"
:key="__index"
class="upload-list"
>
<template>
<img alt="image" :src="item.url"/>
<div class="upload-list-cover">
<div>
<Icon
size="30"
type="md-search"
@click.native="$previewImage(item.url)"
></Icon>
<Icon
size="30"
type="md-trash"
@click.native="handleRemoveGoodsPicture(__index)"
></Icon>
</div>
<img alt="image" :src="item.url" />
<div class="upload-list-cover">
<div>
<el-icon class="action-icon" :size="30" @click="handleView(item.url)">
<ZoomIn />
</el-icon>
<el-icon
class="action-icon"
:size="30"
@click="handleRemoveGoodsPicture(__index)"
>
<Delete />
</el-icon>
</div>
</template>
</div>
</div>
</vuedraggable>
<div class="upload-box">
<Upload
<el-upload
ref="upload"
:action="uploadFileUrl"
:format="['jpg', 'jpeg', 'png']"
:headers="{ ...accessToken }"
:max-size="10240"
:on-exceeded-size="handleMaxSize"
:on-format-error="handleFormatError"
:on-success="handleSuccessGoodsPicture"
:show-upload-list="false"
:headers="accessToken"
:show-file-list="false"
accept=".jpg,.jpeg,.png"
drag
multiple
type="drag"
:before-upload="handleBeforeUpload"
:on-success="handleSuccessGoodsPicture"
:on-error="handleUploadError"
>
<div style="width: 148px; height: 148px; line-height: 148px">
<Icon size="20" type="md-add"></Icon>
<div class="upload-trigger">
<el-icon :size="20"><Plus /></el-icon>
</div>
</Upload>
</el-upload>
</div>
</div>
</Modal>
<template #footer>
<el-button @click="show = false">取消</el-button>
<el-button type="primary" @click="callback">确定</el-button>
</template>
</el-dialog>
<Modal width="1000" v-model="showOssManager" @on-ok="confirmUrls">
<OssManage ref="ossManage" :isComponent="true" :initialize="showOssManager" @selected="(list)=>{ selectedImage = list}" @callback="handleCallback" />
</Modal>
<el-dialog
v-model="showOssManager"
width="1000px"
append-to-body
:z-index="3600"
destroy-on-close
@closed="confirmUrls"
>
<OssManage
ref="ossManage"
:is-component="true"
:initialize="showOssManager"
@selected="(list) => { selectedImage = list }"
@callback="handleCallback"
/>
<template #footer>
<el-button @click="showOssManager = false">取消</el-button>
<el-button type="primary" @click="confirmUrls">确定</el-button>
</template>
</el-dialog>
<el-dialog v-model="viewImage" title="图片预览" width="520px" append-to-body :z-index="3700">
<img :src="previewUrl" alt="预览" style="width: 100%; display: block; margin: 0 auto" />
<template #footer>
<el-button @click="viewImage = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
<script>
import { Delete, Plus, ZoomIn } from "@element-plus/icons-vue";
import vuedraggable from "vuedraggable";
import {uploadFile} from "@/libs/axios";
// import OssManage from "@/views/sys/oss-manage/ossManage";
import { uploadFile } from "@/libs/axios";
import OssManage from "@/views/sys/oss-manage/ossManage.vue";
export default {
@@ -72,16 +92,21 @@ export default {
components: {
OssManage,
vuedraggable,
Delete,
Plus,
ZoomIn,
},
data() {
return {
show: false, // 是否显示弹窗
uploadFileUrl: uploadFile, // 上传地址
accessToken:"",
showOssManager:false, // 是否显示oss管理弹窗
images:[],
selectedImage:[]
}
show: false,
uploadFileUrl: uploadFile,
accessToken: {},
showOssManager: false,
images: [],
selectedImage: [],
viewImage: false,
previewUrl: "",
};
},
mounted() {
this.accessToken = {
@@ -89,71 +114,73 @@ export default {
};
},
methods: {
handleClickUploadImage(){
handleClickUploadImage() {
this.show = true;
},
// 回调给父级
callback() {
// 先给数据做一下处理 然后将数据传给父级
const formatImages = this.images.map((item) => item.url);
this.$emit('callback',formatImages)
handleView(url) {
this.previewUrl = url;
this.viewImage = true;
},
callback() {
const formatImages = this.images.map((item) => item.url);
this.$emit("callback", formatImages);
this.show = false;
},
// 移除商品图片
handleRemoveGoodsPicture(__index) {
this.images.splice(__index, 1);
},
// 图片大小不正确
handleMaxSize(file) {
this.$Notice.warning({
title: "超过文件大小限制",
desc: "图片大小不能超过10MB",
});
handleBeforeUpload(file) {
const okType = ["image/jpeg", "image/png", "image/jpg"].includes(file.type);
if (!okType) {
this.$Message.warning("文件 " + file.name + " 的格式不正确,请选择 jpg/jpeg/png");
return false;
}
if (file.size / 1024 / 1024 > 10) {
this.$Message.warning("图片大小不能超过10MB");
return false;
}
return true;
},
// 图片格式不正确
handleFormatError(file) {
this.$Notice.warning({
title: "文件格式不正确",
desc: "文件 " + file.name + " 的格式不正确",
});
},
// sku图片上传成功
handleSuccessGoodsPicture(res, file) {
if (file.response) {
file.url = file.response.result;
this.images.push(file);
handleSuccessGoodsPicture(res) {
const url = res?.result ?? res?.data?.result;
if (url) {
this.images.push({ url });
} else {
this.$Message.error(res?.message || "上传失败");
}
},
confirmUrls(){
// this.selectedImage.length ? this.selectedImage.forEach(element => {
// this.images.push({ url: element.url })
// }):''
this.showOssManager = false
handleUploadError(err) {
this.$Message.error(err?.message || String(err));
},
handleCallback(val){
this.$Message.success("导入成功")
this.images.push({url:val.url})
confirmUrls() {
this.showOssManager = false;
},
// 从资源库中导入图片
importOSS(){
handleCallback(val) {
this.$Message.success("导入成功");
this.images.push({ url: val.url });
},
importOSS() {
this.showOssManager = true;
this.$refs.ossManage.selectImage = true;
}
}
}
this.$nextTick(() => {
if (this.$refs.ossManage) {
this.$refs.ossManage.selectImage = true;
}
});
},
},
};
</script>
<style scoped lang="scss">
.import-oss{
.import-oss {
margin-bottom: 10px;
text-align: right;
color: $theme_color;
cursor: pointer;
}
.wrapper{
.wrapper {
margin: 10px 0;
}
.upload-list {
width: 150px;
height: 150px;
@@ -166,47 +193,48 @@ export default {
margin-right: 4px;
vertical-align: bottom;
}
.upload-box{
.upload-box {
margin: 10px 0;
display: inline-block;
vertical-align: bottom;
}
.upload-trigger {
width: 148px;
height: 148px;
line-height: 148px;
display: flex;
align-items: center;
justify-content: center;
}
.upload-list img {
width: 100%;
height: 100%;
object-fit: cover;
}
.upload-list-cover {
display: none;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.6);
justify-content: space-between;
align-items: center;
flex-direction: column;
}
.upload-list:hover .upload-list-cover {
display: flex;
}
.upload-list-cover div {
margin-top: 50px;
width: 100%;
>i {
width: 50%;
margin-top: 8px;
color: #fff;
font-size: 20px;
cursor: pointer;
}
display: flex;
justify-content: center;
gap: 8px;
}
.action-icon {
color: #fff;
cursor: pointer;
}
</style>

View File

@@ -51,49 +51,45 @@
<li class="hz-u-square hz-u-square-br" data-pointer="dealBR"></li>
</ul>
<Modal
<el-dialog
v-model="showModal"
title="编辑热区"
draggable
scrollable
:mask="false"
ok-text="保存"
@on-ok="saveZone"
@on-cancel="cancelZone"
:modal="false"
append-to-body
width="520px"
@close="cancelZone"
>
<div>
<div class="hz-edit-img">
<img class="show-image" :src="zoneForm.img" alt />
</div>
<Form :model="zoneForm" :label-width="80">
<!-- <FormItem label="图片链接:">
<Input v-model="zoneForm.img"></Input>
<Button size="small" type="primary" @click="handleSelectImg"
>选择图片</Button
<el-form :model="zoneForm" label-width="80px">
<el-form-item label="跳转链接:">
<el-input type="textarea" v-if="zoneForm.type === 'other' && zoneForm.title === '外部链接'" v-model="zoneForm.link" />
<el-button size="small" type="primary" @click="handleSelectLink"
>选择链接</el-button
>
:v-model="zoneForm.type === 'goods' ? zoneForm.goodsName : zoneForm.link"
</FormItem> -->
<FormItem label="跳转链接:">
<Input type="textarea" v-if="zoneForm.type === 'other' && zoneForm.title === '外部链接'" v-model="zoneForm.link" ></Input>
<Button size="small" type="primary" @click="handleSelectLink"
>选择链接</Button
>
</FormItem>
</Form>
</el-form-item>
</el-form>
</div>
</Modal>
<template #footer>
<el-button @click="cancelZone">取消</el-button>
<el-button type="primary" @click="saveZone">保存</el-button>
</template>
</el-dialog>
<!-- 选择商品链接 -->
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
<!-- 选择图片 -->
<Modal width="1200px" v-model="picModelFlag" footer-hide>
<el-dialog width="1200px" v-model="picModelFlag" append-to-body destroy-on-close>
<ossManage
@callback="callbackSelected"
:isComponent="true"
:initialize="picModelFlag"
ref="ossManage"
/>
</Modal>
<template #footer />
</el-dialog>
</li>
</template>
@@ -179,7 +175,7 @@ export default {
},
// 已选链接
selectedLink(val) {
this.zoneForm.link = this.$options.filters.formatLinkType(val);
this.zoneForm.link = this.$filters.formatLinkType(val);
this.settingZone(val);
this.changeInfo(this.zoneForm);
},
@@ -220,7 +216,10 @@ export default {
break;
}
},
saveZone() {},
saveZone() {
this.showModal = false;
this.changeInfo(this.zoneForm);
},
cancelZone() {
this.showModal = false;
},

View File

@@ -1,13 +1,12 @@
<template>
<Modal
:styles="{ top: '120px' }"
width="800"
@on-cancel="clickClose"
@on-ok="clickOK"
<el-dialog
v-model="flag"
:mask-closable="false"
width="800px"
:close-on-click-modal="false"
title="绘制热区"
scrollable
top="120px"
destroy-on-close
@close="clickClose"
>
<template v-if="flag">
<hotzone
@@ -17,7 +16,11 @@
:image="res.img"
></hotzone>
</template>
</Modal>
<template #footer>
<el-button @click="clickClose">取消</el-button>
<el-button type="primary" @click="clickOK">确定</el-button>
</template>
</el-dialog>
</template>
<script>
import hotzone from "./components/Hotzone.vue";
@@ -57,11 +60,11 @@ export default {
};
</script>
<style scoped lang="scss">
::v-deep .ivu-modal {
:deep(.el-dialog) {
overflow: hidden;
height: 650px !important;
}
::v-deep .ivu-modal-body {
:deep(.el-dialog__body) {
width: 100%;
height: 500px;
overflow: hidden;

View File

@@ -4,46 +4,26 @@
<div class="query-wrapper">
<div class="query-item">
<div>搜索范围</div>
<Input
<el-input
v-model="goodsParams.goodsName"
placeholder="商品名称"
@on-clear="
goodsData = [];
goodsParams.goodsName = '';
goodsParams.pageNumber = 1;
getQueryGoodsList();
"
@on-enter="
() => {
goodsData = [];
goodsParams.pageNumber = 1;
getQueryGoodsList();
}
"
icon="ios-search"
clearable
style="width: 150px"
v-model="goodsParams.goodsName"
@clear="onSearchGoods"
@keyup.enter="onSearchGoods"
/>
</div>
<div class="query-item">
<Cascader
<el-cascader
v-model="category"
:options="skuList"
placeholder="请选择商品分类"
style="width: 250px"
:data="skuList"
></Cascader>
clearable
/>
</div>
<div class="query-item">
<Button
type="primary"
@click="
goodsData = [];
goodsParams.pageNumber = 1;
getQueryGoodsList();
"
icon="ios-search"
>搜索</Button
>
<el-button type="primary" @click="onSearchGoods">搜索</el-button>
</div>
</div>
<div>
@@ -63,27 +43,33 @@
<div class="wap-content-desc">
<div class="wap-content-desc-title">{{ item.goodsName }}</div>
<div class="wap-sku">{{ item.goodsUnit }}<Tag style="margin-left: 10px;" :color="item.salesModel === 'RETAIL' ? 'default' : 'geekblue'">{{item.salesModel === "RETAIL" ? "零售型" : "批发型"}}</Tag></div>
<div class="wap-sku">
{{ item.goodsUnit }}
<el-tag
style="margin-left: 10px"
:type="item.salesModel === 'RETAIL' ? 'info' : 'primary'"
>
{{ item.salesModel === "RETAIL" ? "零售型" : "批发型" }}
</el-tag>
</div>
<div class="wap-content-desc-bottom">
<div>¥{{ item.price | unitPrice }}</div>
<div>{{ $filters.unitPrice(item.price) }}</div>
</div>
</div>
</div>
<Spin size="large" fix v-if="loading"></Spin>
<div v-if="loading" v-loading="loading" class="loading-mask" />
<div v-if="empty" class="empty">暂无商品信息</div>
</div>
<Page
:total="total"
<el-pagination
v-model:current-page="goodsParams.pageNumber"
class="pageration"
@on-change="changePageSize"
:total="total"
:page-size="goodsParams.pageSize"
layout="total, prev, pager, next"
size="small"
show-total
show-elevator
>
</Page>
@current-change="changePageSize"
/>
</div>
</div>
</div>
@@ -145,6 +131,11 @@ export default {
this.init();
},
methods: {
onSearchGoods() {
this.goodsData = [];
this.goodsParams.pageNumber = 1;
this.getQueryGoodsList();
},
changePageSize(v){
this.goodsParams.pageNumber = v;
this.getQueryGoodsList();
@@ -201,7 +192,6 @@ export default {
arr[index] = {
value: grandson.id,
label: grandson.name,
children: "",
};
});
}

View File

@@ -1,5 +1,14 @@
<template>
<Modal :styles="{ top: '120px' }" width="1160" :z-index="10000" @on-cancel="clickClose" @on-ok="clickOK" v-model="flag" :mask-closable="false" scrollable>
<el-dialog
v-model="flag"
width="1160px"
top="120px"
:z-index="10000"
:close-on-click-modal="false"
append-to-body
destroy-on-close
@close="clickClose"
>
<template v-if="flag">
<goodsDialog
@selected="
@@ -21,7 +30,11 @@
class="linkDialog"
/>
</template>
</Modal>
<template #footer>
<el-button @click="clickClose">取消</el-button>
<el-button type="primary" @click="clickOK">确定</el-button>
</template>
</el-dialog>
</template>
<script>
import goodsDialog from "./goods-dialog";
@@ -86,11 +99,7 @@ export default {
};
</script>
<style scoped lang="scss">
::v-deep .ivu-modal {
overflow: hidden;
height: 650px !important;
}
::v-deep .ivu-modal-body {
:deep(.el-dialog__body) {
width: 100%;
height: 500px;
overflow: hidden;

View File

@@ -1,10 +1,9 @@
<template>
<div class="wrapper">
<Tabs :value="wap[0].title" class="tabs">
<TabPane
<el-tabs v-model="activeTab" class="tabs">
<el-tab-pane
:label="item.title"
:name="item.title"
@click="clickTag(item, i)"
v-for="(item, i) in wap"
:key="i"
>
@@ -17,9 +16,8 @@
}
"
/>
</TabPane>
<!-- </template> -->
</Tabs>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
@@ -30,13 +28,16 @@ export default {
components: {
goodsDialog
},
setup() {
return { templateWay };
},
data() {
return {
templateWay, // 模板数据
changed: "", // 变更模板
selected: 0, // 已选数据
selectedLink: "", //选中的链接
wap, // tab标签
activeTab: wap[0]?.title || "",
};
},
watch: {
@@ -122,13 +123,8 @@ export default {
width: 100%;
}
::v-deep .ivu-modal {
overflow: hidden;
height: 650px !important;
}
::v-deep .ivu-modal-body {
width: 100%;
:deep(.el-tabs__content) {
height: 500px;
overflow: hidden;
overflow: auto;
}
</style>

View File

@@ -42,12 +42,12 @@
}
}
::v-deep .ivu-scroll-container {
:deep(.ivu-scroll-container){
width: 100% !important;
height: 400px !important;
}
::v-deep .ivu-scroll-content {
:deep(.ivu-scroll-content){
/* */
display: flex;
flex-wrap: wrap;
@@ -81,7 +81,7 @@
align-items: center;
margin: 10px;
::v-deep img {
:deep(img){
width: 60px;
height: 60px;
text-align: center;

View File

@@ -1,17 +1,18 @@
import { markRaw } from "vue";
import category from "./category.vue";
import shops from "./shops.vue";
import marketing from "./marketing.vue";
import pages from "./pages.vue";
import goods from "../goods-dialog.vue";
import other from "./other.vue";
import special from "./special.vue";
import category from './category.vue'
import shops from './shops.vue'
import marketing from './marketing.vue'
import pages from './pages.vue'
import goods from '../goods-dialog.vue'
import other from './other.vue'
import special from './special.vue'
export default {
pages,
marketing,
shops,
category,
goods,
other,
special
}
pages: markRaw(pages),
marketing: markRaw(marketing),
shops: markRaw(shops),
category: markRaw(category),
goods: markRaw(goods),
other: markRaw(other),
special: markRaw(special),
};

View File

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

View File

@@ -1,142 +1,103 @@
<template>
<div>
<Row :gutter="30">
<Col
span="4"
v-for="(item, index) in linkList"
:key="index"
v-if="
(item.title !== '拼团频道' && item.title !== '签到') ||
$route.name !== 'renovation'
"
>
<div
class="card"
:class="{ active: selectedIndex == index }"
@click="handleLink(item, index)"
<el-row :gutter="30">
<template v-for="(item, index) in linkList" :key="index">
<el-col
v-if="showLinkItem(item)"
:span="4"
>
<Icon size="24" :type="item.icon" />
<p>{{ item.title }}</p>
</div>
</Col>
<!-- 外部链接只有pc端跳转 -->
<Col span="4">
<div
class="card"
:class="{ active: selectedIndex == index }"
@click="handleLink(item, index)"
>
<el-icon :size="24">
<component :is="item.icon" />
</el-icon>
<p>{{ item.title }}</p>
</div>
</el-col>
</template>
<el-col v-if="linkVisible" :span="4">
<div
v-if="linkVisible"
class="card"
:class="{ active: selectedIndex == linkList.length }"
@click="handleLink(linkItem, linkList.length)"
>
<Icon size="24" :type="linkItem.icon" />
<el-icon :size="24">
<component :is="linkItem.icon" />
</el-icon>
<p>{{ linkItem.title }}</p>
</div>
</Col>
</Row>
</el-col>
</el-row>
</div>
</template>
<script>
import { markRaw } from "vue";
import {
House,
ShoppingCart,
Star,
Document,
User,
Promotion,
PriceTag,
Sunny,
VideoCamera,
Share,
ShoppingBag,
Link,
} from "@element-plus/icons-vue";
export default {
data() {
return {
linkList: [
// 链接列表
{
title: "首页",
icon: "md-home",
___type: "home",
},
{
title: "购物车",
icon: "md-cart",
___type: "cart",
},
{
title: "收藏商品",
icon: "md-heart",
___type: "collection",
},
{
title: "我的订单",
icon: "md-document",
___type: "order",
},
{
title: "个人中心",
icon: "md-person",
___type: "user",
},
{
title: "拼团频道",
icon: "md-flame",
___type: "group",
},
{
title: "秒杀频道",
icon: "md-flame",
___type: "seckill",
},
{
title: "领券中心",
icon: "md-pricetag",
___type: "coupon",
},
{
title: "签到",
icon: "md-happy",
___type: "sign",
},
{
title: "小程序直播",
icon: "ios-videocam",
___type: "live",
},
{
title: "砍价",
icon: "md-share-alt",
___type: "kanjia",
},
{
title: "积分商城",
icon: "ios-basket",
___type: "point",
},
{ title: "首页", icon: markRaw(House), ___type: "home" },
{ title: "购物车", icon: markRaw(ShoppingCart), ___type: "cart" },
{ title: "收藏商品", icon: markRaw(Star), ___type: "collection" },
{ title: "我的订单", icon: markRaw(Document), ___type: "order" },
{ title: "个人中心", icon: markRaw(User), ___type: "user" },
{ title: "拼团频道", icon: markRaw(Promotion), ___type: "group" },
{ title: "秒杀频道", icon: markRaw(Promotion), ___type: "seckill" },
{ title: "领券中心", icon: markRaw(PriceTag), ___type: "coupon" },
{ title: "签到", icon: markRaw(Sunny), ___type: "sign" },
{ title: "小程序直播", icon: markRaw(VideoCamera), ___type: "live" },
{ title: "砍价", icon: markRaw(Share), ___type: "kanjia" },
{ title: "积分商城", icon: markRaw(ShoppingBag), ___type: "point" },
],
linkItem: {
title: "外部链接",
icon: "ios-link",
icon: markRaw(Link),
___type: "link",
url: "",
},
linkVisible: true, // 是否显示外部链接
selectedIndex: 9999999, // 已选index
linkVisible: true,
selectedIndex: 9999999,
};
},
created(){
// console.log(window.location.href)
let urls = window.location.href
if(urls.indexOf('/floorList/renovation') != -1){
this.linkList.forEach((items,indexs)=>{
if(items.title == '砍价'){
this.linkList.splice(indexs,1)
}
})
this.linkList.forEach((item,index)=>{
if(item.title == '小程序直播'){
this.linkList.splice(index,1)
}
})
this.linkList.forEach((itemss,indexss)=>{
if(itemss.title == '积分商城'){
this.linkList.splice(indexss,1)
}
})
this.linkVisible = true
}else{
this.linkVisible = false
created() {
const urls = window.location.href;
if (urls.indexOf("/floorList/renovation") != -1) {
this.linkList = this.linkList.filter(
(item) =>
item.title !== "砍价" &&
item.title !== "小程序直播" &&
item.title !== "积分商城"
);
this.linkVisible = true;
} else {
this.linkVisible = false;
}
},
methods: {
showLinkItem(item) {
return (
(item.title !== "拼团频道" && item.title !== "签到") ||
this.$route.name !== "renovation"
);
},
handleLink(val, index) {
val = { ...val, ___type: "other" };
this.selectedIndex = index;
@@ -157,7 +118,7 @@ export default {
text-align: center;
transition: 0.35s;
cursor: pointer;
::v-deep p {
:deep(p) {
margin: 10px 0;
}
border: 1px solid #ededed;

View File

@@ -33,7 +33,7 @@ export default {
};
</script>
<style lang="scss" scoped>
::v-deep .ivu-card-body {
:deep(.ivu-card-body){
height: 414px;
overflow: auto;
}
@@ -69,11 +69,11 @@ export default {
height: 416px;
overflow: hidden;
}
::v-deep .ivu-table {
:deep(.ivu-table){
height: 300px !important;
overflow: auto;
}
::v-deep .ivu-card-body {
:deep(.ivu-card-body){
padding: 0;
height: auto;
}

View File

@@ -4,40 +4,54 @@
<div class="query-wrapper">
<div class="query-item">
<div>店铺名称</div>
<Input placeholder="请输入店铺名称" @on-clear="shopsData=[]; params.storeName=''; params.pageNumber =1; init()" @on-enter="()=>{shopsData=[]; params.pageNumber =1; init();}" icon="ios-search" clearable style="width: 150px"
v-model="params.storeName" />
<el-input
v-model="params.storeName"
placeholder="请输入店铺名称"
clearable
style="width: 150px"
@clear="resetSearch"
@keyup.enter="resetSearch"
/>
</div>
<div class="query-item">
<Button type="primary" @click="shopsData=[];params.pageNumber =1; init();" icon="ios-search">搜索</Button>
<el-button type="primary" @click="resetSearch">搜索</el-button>
</div>
</div>
<div>
<div class="wap-content-list" >
<div class="wap-content-item" @click="clickShop(item,index)" :class="{ active:selected == index }" v-for="(item, index) in shopsData" :key="index">
<div v-loading="loading" class="wap-content-list">
<div
v-for="(item, index) in shopsData"
:key="index"
class="wap-content-item"
:class="{ active: selected == index }"
@click="clickShop(item, index)"
>
<div>
<img class="shop-logo" :src="item.storeLogo" alt="" />
</div>
<div class="wap-content-desc">
<div class="wap-content-desc-title">{{ item.storeName }}</div>
<div class="self-operated" :class="{'theme_color':item.selfOperated }">{{ item.selfOperated ? '自营' : '非自营' }}</div>
<div class="wap-sku" :class="{'theme_color':(item.storeDisable === 'OPEN' ? true : false) }">{{ item.storeDisable === 'OPEN' ? '开启中' : '未开启' }}</div>
<div class="self-operated" :class="{ theme_color: item.selfOperated }">
{{ item.selfOperated ? "自营" : "非自营" }}
</div>
<div
class="wap-sku"
:class="{ theme_color: item.storeDisable === 'OPEN' }"
>
{{ item.storeDisable === "OPEN" ? "开启中" : "未开启" }}
</div>
</div>
</div>
<Spin size="large" fix v-if="loading"></Spin>
</div>
<Page
:total="total"
<el-pagination
class="pageration"
@on-change="changePageSize"
:page-size="params.pageSize"
size="small"
show-total
show-elevator
>
</Page>
layout="total, prev, pager, next, jumper"
:total="total"
:page-size="params.pageSize"
:current-page="params.pageNumber"
@current-change="changePageSize"
/>
</div>
</div>
</div>
@@ -47,25 +61,28 @@ import { getShopListData } from "@/api/shops.js";
export default {
data() {
return {
loading: false, // 加载状态
total: "", // 总数
params: { // 请求参数
loading: false,
total: 0,
params: {
pageNumber: 1,
pageSize: 12,
storeDisable: "OPEN",
storeName: "",
},
shopsData: [], // 店铺数据
selected: 9999999999, //设置一个不可能选中的index
shopsData: [],
selected: 9999999999,
};
},
watch: {},
created() {
this.init();
},
methods: {
changePageSize(v){
resetSearch() {
this.shopsData = [];
this.params.pageNumber = 1;
this.init();
},
changePageSize(v) {
this.params.pageNumber = v;
this.init();
},
@@ -73,15 +90,10 @@ export default {
this.loading = true;
getShopListData(this.params).then((res) => {
if (res.success) {
/**
* 解决数据请求中,滚动栏会一直上下跳动
*/
this.total = res.result.total;
this.shopsData = res.result.records;
this.loading = false;
}
this.loading = false;
});
},
clickShop(val, i) {
@@ -105,15 +117,18 @@ export default {
display: flex;
flex-wrap: wrap;
height: 340px;
min-height: 120px;
}
.shop-logo {
object-fit: cover;
}
.active {
background: url("../../../assets/selected.png") no-repeat;
background-position: right;
background-size: 10%;
}
.pageration {
margin-top: 12px;
justify-content: flex-end;
}
</style>

View File

@@ -3,26 +3,31 @@
<div class="content">
<div>
<div class="tables">
<Table
border
height="350"
tooltip
:loading="loading"
:columns="columns"
:data="data"
>
</Table>
<el-table v-loading="loading" border height="350" :data="data" style="width: 100%">
<el-table-column prop="name" label="专题名称" show-overflow-tooltip />
<el-table-column label="操作" width="100" fixed="right" align="center">
<template #default="{ row, $index }">
<el-button
v-if="row"
:type="index === $index ? 'primary' : 'default'"
size="small"
@click="selectRow(row, $index)"
>
{{ index === $index ? "已选" : "选择" }}
</el-button>
</template>
</el-table-column>
</el-table>
<Page
@on-change="changePageNum"
@on-page-size-change="changePageSize"
:current="params.pageNumber"
:page-size="params.pageSize"
<el-pagination
v-model:current-page="params.pageNumber"
v-model:page-size="params.pageSize"
class="mt_10"
:total="Number(total)"
layout="prev, pager, next, jumper"
size="small"
show-elevator
@current-change="changePageNum"
@size-change="changePageSize"
/>
</div>
</div>
@@ -30,16 +35,13 @@
</div>
</template>
<script>
import { getHomeList } from "@/api/other.js";
import { getHomeList } from "@/api/other.js";
export default {
data() {
return {
loading: true, //表格请求数据为true
promotionList: "", // 活动列表
selectedIndex: 0, //左侧菜单选择
promotions: "", //选中的活动key
index: 999, // 已选下标
data:[],
loading: true,
index: 999,
data: [],
params: {
sort: "createTime",
order: "desc",
@@ -48,68 +50,34 @@ export default {
pageSize: 20,
pageType: "SPECIAL",
},
total: 0, // 表单数据总数
columns: [
{
title: "专题名称",
key: "name",
tooltip: true,
// slot: 'name'
// width: 200,
},
{
title: "操作",
key: "action",
fixed: "right",
width: 100,
render: (h, params) => {
return h("div", [
h(
"Button",
{
props: {
type: this.index == params.index ? "primary" : "default",
size: "small",
},
on: {
click: () => {
this.index = params.index;
params.row = {...params.row,pageType:'special',___type:'special'}
this.$emit("selected", [params.row]);
},
},
},
this.index == params.index ? "已选" : "选择"
),
]);
},
},
],
total: 0,
};
},
mounted() {
this.init();
},
methods: {
changePageNum (val) { // 修改评论页码
selectRow(row, idx) {
this.index = idx;
const payload = { ...row, pageType: "special", ___type: "special" };
this.$emit("selected", [payload]);
},
changePageNum(val) {
this.params.pageNumber = val;
this.init();
},
changePageSize (val) { // 修改评论页数
changePageSize(val) {
this.params.pageNumber = 1;
this.params.pageSize = val;
this.init();
},
// 获取话题的标题
async init() {
// 根据当前路径判断当前是H5还是PC
this.params.pageClientType = this.$route.name === 'renovation' ? 'PC' : 'H5'
let res = await getHomeList(this.params);
this.params.pageClientType = this.$route.name === "renovation" ? "PC" : "H5";
const res = await getHomeList(this.params);
if (res.success) {
this.loading = false;
this.data= res.result.records
this.total = res.result.total
this.data = res.result.records;
this.total = res.result.total;
} else {
this.loading = false;
}
@@ -128,49 +96,17 @@ img {
margin-top: 2vh;
text-align: right;
}
.time {
font-size: 12px;
}
.tables {
height: 400px;
margin-top: 20px;
overflow: auto;
width: 100%;
}
::v-deep .ivu-table-wrapper {
width: 100%;
}
.list {
margin: 0 1.5%;
height: 400px;
overflow: auto;
> .list-item {
padding: 10px;
transition: 0.35s;
cursor: pointer;
}
.list-item:hover {
background: #ededed;
}
}
.list {
flex: 1;
width: auto;
}
.content {
overflow: hidden;
flex: 4;
}
.active {
background: #ededed;
}
.wrapper {
overflow: hidden;
}
.search-views {
display: flex;
> * {
margin: 0 4px;
}
}
</style>

View File

@@ -1,12 +1,17 @@
<template>
<div style="display: inline-block;">
<Icon type="ios-loading" size="18" color="#2d8cf0" class="spin-icon-load"></Icon>
<div style="display: inline-block">
<el-icon class="spin-icon-load" :size="18" color="#ff5c58">
<Loading />
</el-icon>
</div>
</template>
<script>
import { Loading } from "@element-plus/icons-vue";
export default {
name: "circleLoading"
name: "circleLoading",
components: { Loading },
};
</script>
@@ -14,5 +19,12 @@ export default {
.spin-icon-load {
animation: ani-demo-spin 1s linear infinite;
}
@keyframes ani-demo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>

View File

@@ -1,78 +1,34 @@
<template>
<div>
<Drawer width="300px" title="页面配置" v-model="drawer">
<!-- 内容 -->
<h3>
内容设置
</h3>
<div class="config-item flex flex-a-c flex-j-sb">
<div>
<Tooltip theme="light" placement="bottom-end" max-width="100" content="关闭之后部分页面点击'查看''详情'等按钮将跳到新页面展示" >
<div>
多标签Tab页内嵌模式
</div>
</Tooltip>
</div>
<i-switch v-model="setting.isUseTabsRouter"></i-switch>
</div>
</Drawer>
<el-drawer v-model="drawer" title="页面配置" size="300px">
<div class="config-empty">暂无可配置项</div>
</el-drawer>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: "configDrawer",
data() {
return {
drawer: false,
};
},
computed: {
...mapState({
setting: state => {
return state.setting.setting
}
})
},
watch: {
setting: {
handler(val) {
this.setStore('admin-setting', val)
this.$store.commit('updateSetting', val);
},
deep: true
}
},
mounted() {
},
methods: {
open() {
this.drawer = true
this.drawer = true;
},
close() {
this.drawer = false
this.drawer = false;
},
toggle() {
this.drawer != this.drawer
},
}
}
},
};
</script>
<style lang="scss" scoped>
* {
color: #333 !important;
}
h3 {
margin: 10px 0 20px 0;
}
.config-item {
cursor: pointer;
margin-bottom: 20px;
justify-content: space-between;
<style scoped>
.config-empty {
color: #909399;
text-align: center;
padding: 20px 0;
}
</style>

View File

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

View File

@@ -1,106 +1,110 @@
<template>
<div>
<div style="display:flex;">
<Input
<div style="display: flex">
<el-input
v-model="departmentTitle"
readonly
style="margin-right:10px;"
style="margin-right: 10px; flex: 1"
:placeholder="placeholder"
:clearable="clearable"
@on-clear="clearSelect"
@clear="clearSelect"
/>
<Poptip transfer trigger="click" placement="right" title="选择部门" width="250">
<Button icon="md-list">选择部门</Button>
<div slot="content">
<Input
v-model="searchKey"
suffix="ios-search"
@on-change="searchDep"
placeholder="输入部门名搜索"
clearable
<el-popover trigger="click" placement="right" title="选择部门" :width="280">
<template #reference>
<el-button>选择部门</el-button>
</template>
<el-input
v-model="searchKey"
placeholder="输入部门名搜索"
clearable
style="margin-bottom: 8px"
@input="searchDep"
/>
<div v-loading="depLoading" class="dep-tree-bar">
<el-tree
:data="dataDep"
:props="treeProps"
node-key="id"
highlight-current
default-expand-all
@node-click="selectTree"
/>
<div class="dep-tree-bar">
<Tree
:data="dataDep"
@on-select-change="selectTree"
></Tree>
<Spin size="large" fix v-if="depLoading"></Spin>
</div>
</div>
</Poptip>
</el-popover>
</div>
</div>
</template>
<script>
import {initDepartment, searchDepartment} from "@/api/index";
import { initDepartment } from "@/api/index";
export default {
name: "departmentTreeChoose",
props: {
multiple: {
type: Boolean,
default: false
default: false,
},
clearable: {
type: Boolean,
default: true
default: true,
},
placeholder: {
type: String,
default: "点击选择部门"
}
default: "点击选择部门",
},
},
data() {
return {
depLoading: false, // 加载状态
departmentTitle: "", // modal标题
searchKey: "", // 搜索关键词
dataDep: [], // 部门列表
cloneDep: [], // 克隆部门列表
selectDep: [], // 已选部门
departmentId: [] // 部门id
depLoading: false,
departmentTitle: "",
searchKey: "",
dataDep: [],
cloneDep: [],
departmentId: [],
treeProps: {
label: "title",
children: "children",
},
};
},
methods: {
// 获取部门数据
initDepartmentData() {
initDepartment().then(res => {
if (res.success) {
this.dataDep = res.result;
this.cloneDep = JSON.parse(JSON.stringify(this.dataDep));
}
});
this.depLoading = true;
initDepartment()
.then((res) => {
if (res.success) {
this.dataDep = res.result;
this.cloneDep = JSON.parse(JSON.stringify(this.dataDep));
}
})
.finally(() => {
this.depLoading = false;
});
},
searchDep() {
// 搜索部门
if (this.searchKey) {
this.dataDep = this.cloneDep.filter(item => {
return item.title.indexOf(this.searchKey) > -1;
});
this.dataDep = this.cloneDep.filter(
(item) => item.title.indexOf(this.searchKey) > -1
);
} else {
this.dataDep = JSON.parse(JSON.stringify(this.cloneDep));
}
},
// 选择回调
selectTree(v) {
if (v.length === 0) {
selectTree(node) {
if (!node) {
this.$emit("on-change", null);
this.departmentId = "";
this.departmentTitle = "";
return
return;
}
this.departmentId = v[0].id;
this.departmentTitle = v[0].title;
let department = {
this.departmentId = node.id;
this.departmentTitle = node.title;
this.$emit("on-change", {
departmentId: this.departmentId,
departmentTitle: this.departmentTitle
}
this.$emit("on-change", department);
departmentTitle: this.departmentTitle,
});
},
// 清除选中方法
clearSelect() {
this.departmentId = [];
this.departmentTitle = "";
@@ -112,7 +116,6 @@ export default {
}
this.$emit("on-clear");
},
// 设置数据 回显用
setData(ids, title) {
this.departmentTitle = title;
if (this.multiple) {
@@ -121,11 +124,11 @@ export default {
this.departmentId = [];
this.departmentId.push(ids);
}
}
},
},
created() {
this.initDepartmentData();
}
},
};
</script>
@@ -145,9 +148,6 @@ export default {
.dep-tree-bar::-webkit-scrollbar-thumb {
border-radius: 4px;
-webkit-box-shadow: inset 0 0 2px #d1d1d1;
background: #e4e4e4;
}
</style>

View File

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

View File

@@ -1,210 +1,166 @@
<template>
<div>
<div style="display:flex;">
<Input
<div style="display: flex; gap: 10px; align-items: center; width: 100%">
<el-input
v-if="showInput"
v-model="currentValue"
@on-change="handleChange"
v-show="showInput"
:placeholder="placeholder"
:size="size"
:disabled="disabled"
:readonly="readonly"
:maxlength="maxlength"
style="flex: 1"
@input="handleChange"
>
<Poptip slot="append" transfer trigger="hover" title="图片预览" placement="right">
<Icon type="md-eye" class="see-icon" />
<div slot="content">
<img :src="currentValue" alt="该资源不存在" style="max-width: 300px;margin: 0 auto;display: block;" />
<a @click="viewImage=true" style="margin-top:5px;text-align:right;display:block">查看大图</a>
</div>
</Poptip>
</Input>
<Button @click="handleCLickImg('storeLogo')">选择图片</Button>
<!--<Upload-->
<!--:action="uploadFileUrl"-->
<!--:headers="accessToken"-->
<!--:on-success="handleSuccess"-->
<!--:on-error="handleError"-->
<!--:format="['jpg','jpeg','png','gif','bmp']"-->
<!--accept=".jpg, .jpeg, .png, .gif, .bmp"-->
<!--:max-size="1024"-->
<!--:on-format-error="handleFormatError"-->
<!--:on-exceeded-size="handleMaxSize"-->
<!--:before-upload="beforeUpload"-->
<!--:show-upload-list="false"-->
<!--ref="up"-->
<!--class="upload"-->
<!--&gt;-->
<!--<Button :loading="loading" :size="size" :disabled="disabled">上传图片</Button>-->
<!--</Upload>-->
<template #append>
<el-popover trigger="hover" placement="right" :width="320" title="图片预览">
<template #reference>
<el-button class="see-icon">
<el-icon><View /></el-icon>
</el-button>
</template>
<img
v-if="currentValue"
:src="currentValue"
alt="该资源不存在"
style="max-width: 280px; display: block; margin: 0 auto"
/>
<el-button
v-if="currentValue"
type="primary"
link
style="margin-top: 8px; display: block; text-align: right"
@click="viewImage = true"
>
查看大图
</el-button>
</el-popover>
</template>
</el-input>
<el-button @click="handleCLickImg('storeLogo')">选择图片</el-button>
</div>
<Modal
title="图片预览"
v-model="viewImage"
:styles="{ top: '30px' }"
:z-index="3500"
draggable
>
<img :src="currentValue" alt="该资源不存在" style="max-width: 300px;margin: 0 auto;display: block;" />
<div slot="footer">
<Button @click="viewImage=false">关闭</Button>
</div>
</Modal>
<Modal width="1200px" v-model="picModalFlag" :z-index="3500">
<ossManage @callback="callbackSelected" ref="ossManage" :isComponent="true" :initialize="picModalFlag" />
</Modal>
<el-dialog v-model="viewImage" title="图片预览" width="480px" append-to-body :z-index="3500">
<img
:src="currentValue"
alt="该资源不存在"
style="max-width: 100%; margin: 0 auto; display: block"
/>
<template #footer>
<el-button @click="viewImage = false">关闭</el-button>
</template>
</el-dialog>
<el-dialog v-model="picModalFlag" width="1200px" append-to-body :z-index="3500" destroy-on-close>
<ossManage
ref="ossManage"
:is-component="true"
:initialize="picModalFlag"
@callback="callbackSelected"
/>
</el-dialog>
</div>
</template>
<script>
import { View } from "@element-plus/icons-vue";
import { uploadFile } from "@/api/index";
import ossManage from "@/views/sys/oss-manage/ossManage";
export default {
name: "uploadPicInput",
components: {
ossManage,
View,
},
props: {
modelValue: String,
value: String,
size: {
default: 'default',
type: String
default: "default",
type: String,
},
placeholder: {
type: String,
default: "图片链接"
default: "图片链接",
},
showInput: {
type: Boolean,
default: true
default: true,
},
disabled: {
type: Boolean,
default: false
default: false,
},
readonly: {
type: Boolean,
default: false
default: false,
},
maxlength: Number,
icon: {
type: String,
default: "ios-cloud-upload-outline"
}
},
emits: ["update:modelValue", "input", "on-change"],
data() {
return {
accessToken: {}, // 验证token
currentValue: this.value, // 当前值
loading: false, // 加载状态
viewImage: false, // 预览图片modal
uploadFileUrl: uploadFile, // 上传地址
picModalFlag: false, // 图片选择器
selectedFormBtnName: "", // 点击图片绑定form
picIndex: "", // 存储身份证图片下标,方便赋值
accessToken: {},
currentValue: this.modelValue ?? this.value ?? "",
viewImage: false,
uploadFileUrl: uploadFile,
picModalFlag: false,
selectedFormBtnName: "",
picIndex: "",
};
},
methods: {
// 选择图片modal
handleCLickImg(val, index) {
this.$refs.ossManage.selectImage = true;
this.picModalFlag = true;
this.selectedFormBtnName = val;
this.picIndex = index;
},
// 图片回显
callbackSelected(val) {
this.picModalFlag = false;
this.currentValue = val.url;
this.picIndex = "";
this.$emit("input", this.currentValue);
this.$emit("on-change", this.currentValue);
this.emitValue(this.currentValue);
},
// 初始化
init() {
this.accessToken = {
accessToken: this.getStore("accessToken")
accessToken: this.getStore("accessToken"),
};
},
// 格式校验
handleFormatError(file) {
this.loading = false;
this.$Notice.warning({
title: "不支持的文件格式",
desc:
"所选文件‘ " +
file.name +
" ’格式不正确, 请选择 .jpg .jpeg .png .gif .bmp格式文件"
});
emitValue(val) {
this.$emit("update:modelValue", val);
this.$emit("input", val);
this.$emit("on-change", val);
},
// 大小校验
handleMaxSize(file) {
this.loading = false;
this.$Notice.warning({
title: "文件大小过大",
desc: "所选文件大小过大, 不得超过1M."
});
handleChange() {
this.emitValue(this.currentValue);
this.$attrs.rollback && this.$attrs.rollback();
},
// 上传前
beforeUpload() {
this.loading = true;
return true;
},
// 上传成功
handleSuccess(res, file) {
this.loading = false;
if (res.success) {
this.currentValue = res.result;
this.$emit("input", this.currentValue);
this.$emit("on-change", this.currentValue);
} else {
// this.$Message.error(res.message);
}
},
// 上传失败
handleError(error, file, fileList) {
this.loading = false;
this.$Message.error(error.toString());
},
// 上传成功回显
handleChange(v) {
this.$emit("input", this.currentValue);
this.$emit("on-change", this.currentValue);
this.$attrs.rollback && this.$attrs.rollback()
},
// 初始值
setCurrentValue(value) {
if (value === this.currentValue) {
return;
}
this.currentValue = value;
this.$emit("input", this.currentValue);
this.$emit("on-change", this.currentValue);
}
this.currentValue = value ?? "";
this.emitValue(this.currentValue);
},
},
watch: {
modelValue(val) {
this.setCurrentValue(val);
},
value(val) {
this.setCurrentValue(val);
}
},
},
created() {
this.init();
}
},
};
</script>
<style lang="scss" scoped>
.see-icon {
font-size: 16px;
cursor: pointer;
}
.upload {
display: inline-block;
margin-left: 10px;
padding: 8px;
}
</style>

View File

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

View File

@@ -5,7 +5,7 @@
<div id="map-container"></div>
<div class="search-con">
<Input placeholder="输入关键字搜索" id="input-map" v-model="mapSearch" />
<el-input id="input-map" v-model="mapSearch" placeholder="输入关键字搜索" clearable />
<ul>
<li v-for="(tip, index) in tips" :key="index" @click="selectAddr(tip.location)">
<p>{{ tip.name }}</p>
@@ -13,9 +13,8 @@
</li>
</ul>
</div>
<div slot="footer" class="footer">
<Button type="primary" :loading="loading" @click="ok">确定</Button>
<div class="footer">
<el-button type="primary" :loading="loading" @click="ok">确定</el-button>
</div>
</div>

View File

@@ -1,38 +1,50 @@
<template>
<Modal width="800" footer-hide v-model="enableMap">
<RadioGroup @on-change="changeMap" v-model="mapDefault" type="button">
<Radio label="select">级联选择</Radio>
<Radio label="map" v-if="aMapSwitch">高德地图</Radio>
</RadioGroup>
<el-dialog v-model="enableMap" width="800px" :show-close="true" destroy-on-close>
<el-radio-group v-model="mapDefault" @change="changeMap">
<el-radio-button value="select">级联选择</el-radio-button>
<el-radio-button v-if="aMapSwitch" value="map">高德地图</el-radio-button>
</el-radio-group>
<div>
<div v-if="mapDefault === 'select'">
<div class="selector">
<div class="selector-item" v-for="(plant, plantIndex) in Object.keys(data)" :key="plantIndex">
<div :class="{ 'active': chiosend[plantIndex].id == item.id }" v-for="(item, index) in data[plant]"
<div
v-for="(plant, plantIndex) in Object.keys(data)"
:key="plantIndex"
class="selector-item"
>
<div
v-for="(item, index) in data[plant]"
:key="index"
@click="init(item, plantIndex != Object.keys(data).length - 1 ? Object.keys(data)[plantIndex + 1] : 0, plantIndex)"
class="map-item">
:class="{ active: chiosend[plantIndex]?.id == item.id }"
class="map-item"
@click="
init(
item,
plantIndex != Object.keys(data).length - 1
? Object.keys(data)[plantIndex + 1]
: 0,
plantIndex
)
"
>
{{ item.name }}
</div>
</div>
</div>
<div class="footer">
<Button type="primary" @click="finished">确定</Button>
<el-button type="primary" @click="finished">确定</el-button>
</div>
</div>
<mapping v-if="mapDefault === 'map'" ref="map" @getAddress="getAddress" />
</div>
</Modal>
</el-dialog>
</template>
<script>
import { aMapSwitch } from '@/config/index'
import { aMapSwitch } from "@/config/index";
import mapping from "@/components/map/index.vue";
import * as API_Setup from "@/api/common.js";
export default {
components: { mapping },
data() {
@@ -41,87 +53,76 @@ export default {
enableMap: false,
mapDefault: "select",
data: {
province: [], //省
city: [], //市
area: [], //区
street: [], //街道
province: [],
city: [],
area: [],
street: [],
},
chiosend: [],
};
},
mounted() {
this.chiosend = new Array(4).fill("");
},
methods: {
open() {
this.enableMap = true
this.init({ id: 0 }, 'province');
this.enableMap = true;
this.init({ id: 0 }, "province");
},
changeMap(val) {
this.mapDefault = val
this.mapDefault = val;
},
init(val, level = 'province', index) {
init(val, level = "province", index) {
if (level == 0) {
// 说明选择到了街道将街道id存入数组
this.chiosend.splice(3, 1, val);
}
else {
} else {
API_Setup.getChildRegion(val.id).then((res) => {
if (res.result.length && val.id !== 0) {
this.chiosend[index] = val
}
else if(!res.result.length){
this.chiosend[index] = val
this.chiosend[index] = val;
} else if (!res.result.length) {
this.chiosend[index] = val;
}
this.data[level] = res.result;
if (level == 'city') {
this.data.area = []
this.data.street = []
this.chiosend.splice(1, 3, "","","");
if (level == "city") {
this.data.area = [];
this.data.street = [];
this.chiosend.splice(1, 3, "", "", "");
}
if (level == 'area') {
this.data.street = []
this.chiosend.splice(2, 2, "","");
if (level == "area") {
this.data.street = [];
this.chiosend.splice(2, 2, "", "");
}
if (level == 'street') {
if (level == "street") {
this.chiosend.splice(3, 1, "");
}
});
}
},
getAddress(center) {
this.$emit('callback', {
this.$emit("callback", {
type: this.mapDefault,
data: center
})
data: center,
});
this.enableMap = false;
},
// 选择完成
finished() {
if(!this.chiosend[0]){
this.$Message.error("请选择地址")
return
if (!this.chiosend[0]) {
this.$Message.error("请选择地址");
return;
}
const params = this.chiosend.filter((item) => item!=="" && item.value !== "");
const params = this.chiosend.filter((item) => item !== "" && item.value !== "");
this.enableMap = false;
this.$emit('callback', {
this.$emit("callback", {
type: this.mapDefault,
data: params
})
data: params,
});
},
},
}
};
</script>
<style lang="scss" scoped>
.selector {
height: 400px;
padding: 10px 0;
display: flex;

View File

@@ -0,0 +1,58 @@
<template>
<span :style="priceStyle">
{{ dot }}{{ displayText }}
<slot />
</span>
</template>
<script>
import { unitPrice } from "@/utils/filters";
export default {
name: "priceColorScheme",
props: {
value: {
default: 0,
validator(val) {
return (
val === null ||
val === undefined ||
typeof val === "number" ||
typeof val === "string"
);
},
},
unit: {
type: String,
default: "¥",
},
dot: {
type: String,
default: "",
},
color: {
type: String,
default: "",
},
customStyle: {
type: Object,
default: () => ({}),
},
},
computed: {
displayText() {
const val = this.value;
if (val === null || val === undefined || val === "" || val === "null") {
return `${this.unit || "¥"}0.00`;
}
return unitPrice(val, this.unit);
},
priceStyle() {
const resolvedColor = this.color || this.$mainColor || "";
return resolvedColor
? { color: resolvedColor, ...this.customStyle }
: { ...this.customStyle };
},
},
};
</script>

View File

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

View File

@@ -2,8 +2,8 @@
<div
v-if="columns.length > 0"
ref="table"
v-loading="loading"
:class="[prefixCls, `${prefixCls}-${size}`, tableClass]">
<Spin fix v-if="loading"></Spin>
<div
v-show="showHeader"
ref="header-wrapper"
@@ -297,6 +297,11 @@ export default {
cellStyle: [Object, Function],
expandKey: String
},
provide() {
return {
treeTableRoot: this,
};
},
data() {
return {
computedWidth: "",
@@ -387,7 +392,7 @@ export default {
this.measure();
window.addEventListener("resize", this.measure);
},
beforeDestroy() {
beforeUnmount() {
window.removeEventListener("resize", this.measure);
}
};

View File

@@ -1,13 +1,13 @@
import Checkbox from '../Checkbox/Checkbox'; // eslint-disable-line
// import Radio from '../Radio/Radio'; // eslint-disable-line
import { mixins } from './utils';
import { Radio } from 'view-design'; // eslint-disable-line
/* eslint-disable no-underscore-dangle */
export default {
name: 'TreeTable__body',
mixins: [mixins],
components: { Radio },
inject: {
treeTableRoot: { default: null },
},
data() {
return {
radioSelectedIndex: -1,
@@ -15,6 +15,12 @@ export default {
},
computed: {
table() {
if (this.treeTableRoot) return this.treeTableRoot;
let parent = this.$parent;
while (parent) {
if (parent.$options && parent.$options.name === 'TreeTable') return parent;
parent = parent.$parent;
}
return this.$parent;
},
},
@@ -186,8 +192,21 @@ export default {
return classList.join(' ');
}
// Vue 3scoped slot 合并到 $slots
function renderTemplateSlot(table, slotName, scope) {
if (!table || !slotName) return '';
const slots = table.$slots || {};
let slot = slots[slotName];
if (!slot && table.$scopedSlots && table.$scopedSlots[slotName]) {
slot = table.$scopedSlots[slotName];
}
if (!slot) return '';
return slot(scope);
}
// 根据type渲染单元格Cell
function renderCell(row, rowIndex, column, columnIndex) {
if (!row || !column) return '';
// ExpandType
if (this.isExpandCell(this.table, columnIndex)) {
return <i class='zk-icon zk-icon-angle-right'></i>;
@@ -226,7 +245,15 @@ export default {
// onOn-change={isChecked => this.handleEvent(null, 'checkbox', { row, rowIndex, column, columnIndex }, { isChecked })}>
// </Checkbox>;
} else {
res = <Radio value={this.radioSelectedIndex === rowIndex} on-on-change={() => this.handleEvent(null, 'radio', { row, rowIndex, column, columnIndex })}></Radio>;
res = (
<input
type="radio"
checked={this.radioSelectedIndex === rowIndex}
onChange={() =>
this.handleEvent(null, 'radio', { row, rowIndex, column, columnIndex })
}
/>
);
}
return res;
}
@@ -254,9 +281,12 @@ export default {
if (column.type === undefined || column.type === 'custom') {
return row[column.key];
} else if (column.type === 'template') {
return this.table.$scopedSlots[column.template]
? this.table.$scopedSlots[column.template]({ row, rowIndex, column, columnIndex })
: '';
return renderTemplateSlot.call(this, this.table, column.template, {
row,
rowIndex,
column,
columnIndex,
});
}
return '';
}
@@ -305,10 +335,7 @@ export default {
<td
class={`${this.prefixCls}--expand-content`}
colspan={this.table.tableColumns.length}>
{this.table.$scopedSlots.expand
? this.table.$scopedSlots.expand({ row, rowIndex })
: ''
}
{renderTemplateSlot.call(this, this.table, 'expand', { row, rowIndex })}
</td>
</tr>,
])

View File

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

View File

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