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

59
manager/MIGRATION-VUE3.md Normal file
View File

@@ -0,0 +1,59 @@
# manager Vue 3 + Element Plus 迁移说明
## 已完成P1P3 骨架)
- 依赖升级:`vue@3``vue-router@4``vuex@4``vue-i18n@9``element-plus`
- 入口:`src/main.js``createApp`
- 路由、动态路由、全局 `$Message` / `$Modal` / `$Notice``$filters``priceColorScheme``vue-qr@5`
- TinyMCE移除已弃用 `template` 插件
## P5 批量修复Table / 过滤器)
列表页 **60+**`el-table`;过滤器已改为 `$filters.*`;全项目已无 `$set` / `$options.filters`
## P6 表单 / 弹窗(已完成模块)
| 模块 | 状态 |
|------|------|
| 地区、秒杀设置、分销设置、修改密码、编辑器、链接弹窗 | ✅ |
| 店铺详情 / 店铺编辑 `shopOperation.vue` | ✅ |
| 售后详情 `afterSaleOrderDetail.vue` | ✅ |
| **系统设置** `sys/setting-manage/**`19 个 vue + template.js | ✅ |
| **菜单 / 部门** `sys/menu-manage/``sys/department-manage/` | ✅ |
| **页面装修** `views/page-decoration/**`PC + H5 装修、modelList、wap 模板) | ✅ |
| **文章管理** `views/page/article-manage/**` | ✅ |
| **零散页面** 礼品卡、个人中心、热区、促销编辑、视频号、错误页、短信签名等 | ✅ |
### 系统设置包含
- `settingManage.vue`Tab 壳)
- `platformSetting.vue`
- `setting/`基础、商品、订单、积分、提现、物流、OSS、短信、客服
- `pay/`:支付开关、支付宝、微信
- `authLogin/`登录、QQ、微信
- `smsSettingManage.vue`
`template.js` 中动态组件已 `markRaw`
## 可选后续
- `components/tree-table`:旧 JSX 表格,当前路由未引用;`Spin` 已改 `v-loading``beforeUnmount` 已对齐 Vue 3
- 样式文件中残留的 `.ivu-*` 类名可随页面改版逐步清理(不影响运行)
## 编译告警处理2026-05
- 全项目 `::v-deep` 已改为 Vue 3 推荐的 `:deep(...)`
- `common.scss` 合并 `table-common.scss`,去掉全局 `@import` 注入告警
- `vue.config.js` 配置 `sassOptions.silenceDeprecations` 抑制 Sass legacy API 提示
- `common.scss``.search > .el-card + .el-card` 恢复列表页双 Card 间距(原 `.ivu-card` 规则已失效)
- `element.scss`:全局 `el-table--border` 去掉列竖线
## 本地运行
```bash
cd manager
yarn install
yarn run dev
```
Node **18+**,包管理统一 **yarn**

View File

@@ -1,10 +1,3 @@
module.exports = {
presets: [
[
'@vue/app',
{
useBuiltIns: 'entry'
}
]
]
}
presets: ["@vue/cli-plugin-babel/preset"],
};

View File

@@ -10,51 +10,51 @@
"dev": "vue-cli-service serve"
},
"engines": {
"node": ">=14"
"node": ">=16"
},
"dependencies": {
"@amap/amap-jsapi-loader": "0.0.7",
"@antv/g2": "^4.1.12",
"@element-plus/icons-vue": "^2.3.1",
"axios": "^0.21.1",
"core-js": "^3.6.5",
"core-js": "^3.36.0",
"dplayer": "^1.26.0",
"element-plus": "^2.6.3",
"js-cookie": "^2.2.1",
"nprogress": "^0.2.0",
"price-color": "1.0.2",
"sass": "^1.63.6",
"sass-loader": "^10.4.1",
"sockjs-client": "^1.4.0",
"swiper": "^6.3.5",
"uuid": "^8.3.2",
"view-design": "^4.7.0",
"vue": "^2.6.10",
"vue": "^3.4.21",
"vue-awesome-swiper": "^4.1.1",
"vue-i18n": "^8.15.1",
"vue-i18n": "^9.10.2",
"vue-json-excel": "^0.3.0",
"vue-print-nb": "^1.7.5",
"vue-qr": "^2.3.0",
"vue-router": "^3.1.3",
"vuedraggable": "^2.23.2",
"vuex": "^3.4.0",
"vue-qr": "^5.0.3",
"vue-router": "^4.3.0",
"vuedraggable": "^4.1.0",
"vuex": "^4.1.0",
"xss": "^1.0.7"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.4.4",
"@vue/cli-plugin-router": "^4.4.4",
"@vue/cli-plugin-vuex": "^4.4.4",
"@vue/cli-service": "^4.4.4",
"compression-webpack-plugin": "^4.0.0",
"css-loader": "^5.0.1",
"less": "^3.12.2",
"less-loader": "^6.2.0",
"style-loader": "^2.0.0",
"style-resources-loader": "^1.3.2",
"uglifyjs-webpack-plugin": "^2.2.0",
"vue-cli-plugin-style-resources-loader": "^0.1.4",
"vue-template-compiler": "2.6.14"
"webpack": "^5.95.0",
"@vue/cli-plugin-babel": "^5.0.8",
"@vue/cli-plugin-router": "^5.0.8",
"@vue/cli-plugin-vuex": "^5.0.8",
"@vue/cli-service": "^5.0.8",
"@vue/compiler-sfc": "^3.4.21",
"compression-webpack-plugin": "^10.0.0",
"css-loader": "^6.10.0",
"less": "^4.2.0",
"less-loader": "^11.1.4",
"style-loader": "^3.3.4",
"style-resources-loader": "^1.5.0",
"vue-cli-plugin-style-resources-loader": "^0.1.5"
},
"resolutions": {
"minimatch": "^3.1.2",
"node-sass": "npm:sass@^1.63.6",
"@achrinza/node-ipc": "9.2.2"
}
}

View File

@@ -5,7 +5,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name='description' content='在线购物平台'>
<!-- <meta name="viewport" content="width=device-width,initial-scale=1.0"> -->
<link rel="icon" href="./logo.ico" type="image/x-icon" />
<!-- favicon 可选:将 logo.ico 放到 public/ 后取消下行注释 -->
<!-- <link rel="icon" href="/logo.ico" type="image/x-icon" /> -->
<title>admin</title>
<meta name="keywords" content="keywords" />
<meta name="description" content="description" />
@@ -72,7 +73,7 @@
<script src="<%=js%>"></script>
<% } %>
<script src="/config.js"></script>
<script src="./tinymce/tinymce.min.js"></script>
<script src="/tinymce/tinymce.min.js"></script>
<noscript>
<strong
>We're sorry but admin doesn't work properly without JavaScript

View File

@@ -1,32 +1,46 @@
<template>
<div id="main" class="app-main">
<router-view></router-view>
<router-view />
</div>
</template>
<script>
import { getCategoryTree } from '@/api/goods.js'
import Cookies from "js-cookie";
import { getCategoryTree } from "@/api/goods.js";
import util from "@/libs/util";
export default {
name: "App",
updated() {
if (!localStorage.getItem('category') && this.$route.path !== '/login') {
getCategoryTree(0).then(res => {
if (!localStorage.getItem("category") && this.$route.path !== "/login") {
getCategoryTree(0).then((res) => {
if (res.success) {
localStorage.setItem('category', JSON.stringify(res.result))
localStorage.setItem("category", JSON.stringify(res.result));
}
})
});
}
},
mounted() {
const loggedIn = this.getStore("accessToken") || Cookies.get("userInfoManager");
if (loggedIn) {
util.bootstrapDynamicRoutesFromCache();
util.initRouter(this);
this.$store.commit("setOpenedList");
this.$store.commit("initCachePage");
}
},
};
</script>
<style>
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: #f0f0f0;
font-size: 12px;
font-size: 14px;
}
.app-main {
@@ -42,14 +56,6 @@ body {
margin-right: 5px;
}
.ivu-btn-text:focus {
box-shadow: none !important;
}
.ivu-tag {
cursor: pointer;
}
.tox-notifications-container {
display: none !important;
}

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,52 +123,43 @@ 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) => {
res.selected = false;
if (res.title === item.title) {
currentIndex = index
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 {
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>
<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>
</div>
</template>
</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 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管理弹窗
show: false,
uploadFileUrl: uploadFile,
accessToken: {},
showOssManager: false,
images: [],
selectedImage:[]
}
selectedImage: [],
viewImage: false,
previewUrl: "",
};
},
mounted() {
this.accessToken = {
@@ -92,54 +117,58 @@ export default {
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",
});
},
// 图片格式不正确
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);
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;
},
handleSuccessGoodsPicture(res) {
const url = res?.result ?? res?.data?.result;
if (url) {
this.images.push({ url });
} else {
this.$Message.error(res?.message || "上传失败");
}
},
handleUploadError(err) {
this.$Message.error(err?.message || String(err));
},
confirmUrls() {
// this.selectedImage.length ? this.selectedImage.forEach(element => {
// this.images.push({ url: element.url })
// }):''
this.showOssManager = false
this.showOssManager = false;
},
handleCallback(val) {
this.$Message.success("导入成功")
this.images.push({url:val.url})
this.$Message.success("导入成功");
this.images.push({ url: val.url });
},
// 从资源库中导入图片
importOSS() {
this.showOssManager = true;
this.$nextTick(() => {
if (this.$refs.ossManage) {
this.$refs.ossManage.selectImage = true;
}
}
}
});
},
},
};
</script>
<style scoped lang="scss">
@@ -148,12 +177,10 @@ export default {
text-align: right;
color: $theme_color;
cursor: pointer;
}
.wrapper {
margin: 10px 0;
}
.upload-list {
width: 150px;
height: 150px;
@@ -166,47 +193,48 @@ export default {
margin-right: 4px;
vertical-align: bottom;
}
.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;
display: flex;
justify-content: center;
gap: 8px;
}
.action-icon {
color: #fff;
font-size: 20px;
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'
"
<el-row :gutter="30">
<template v-for="(item, index) in linkList" :key="index">
<el-col
v-if="showLinkItem(item)"
:span="4"
>
<div
class="card"
:class="{ active: selectedIndex == index }"
@click="handleLink(item, index)"
>
<Icon size="24" :type="item.icon" />
<el-icon :size="24">
<component :is="item.icon" />
</el-icon>
<p>{{ item.title }}</p>
</div>
</Col>
<!-- 外部链接只有pc端跳转 -->
<Col span="4">
</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
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
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>
<Spin size="large" fix v-if="loading"></Spin>
</div>
<Page
:total="total"
class="pageration"
@on-change="changePageSize"
:page-size="params.pageSize"
size="small"
show-total
show-elevator
<div
class="wap-sku"
:class="{ theme_color: item.storeDisable === 'OPEN' }"
>
</Page>
{{ item.storeDisable === "OPEN" ? "开启中" : "未开启" }}
</div>
</div>
</div>
</div>
<el-pagination
class="pageration"
size="small"
layout="total, prev, pager, next, jumper"
:total="total"
:page-size="params.pageSize"
:current-page="params.pageNumber"
@current-change="changePageSize"
/>
</div>
</div>
</div>
@@ -47,24 +61,27 @@ 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: {
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"
<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)"
>
</Table>
{{ 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>
@@ -34,11 +39,8 @@ import { getHomeList } from "@/api/other.js";
export default {
data() {
return {
loading: true, //表格请求数据为true
promotionList: "", // 活动列表
selectedIndex: 0, //左侧菜单选择
promotions: "", //选中的活动key
index: 999, // 已选下标
loading: true,
index: 999,
data: [],
params: {
sort: "createTime",
@@ -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 => {
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
<el-popover trigger="click" placement="right" title="选择部门" :width="280">
<template #reference>
<el-button>选择部门</el-button>
</template>
<el-input
v-model="searchKey"
suffix="ios-search"
@on-change="searchDep"
placeholder="输入部门名搜索"
clearable
style="margin-bottom: 8px"
@input="searchDep"
/>
<div class="dep-tree-bar">
<Tree
<div v-loading="depLoading" class="dep-tree-bar">
<el-tree
:data="dataDep"
@on-select-change="selectTree"
></Tree>
<Spin size="large" fix v-if="depLoading"></Spin>
:props="treeProps"
node-key="id"
highlight-current
default-expand-all
@node-click="selectTree"
/>
</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 => {
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"
<el-popover trigger="focus" placement="right" :width="250">
<template #reference>
<el-input
v-model="currentValue"
@on-change="handleChange"
type="password"
show-password
style="width: 350px"
:maxlength="maxlength"
:size="size"
:placeholder="placeholder"
:disabled="disabled"
:readonly="readonly"
@input="handleChange"
/>
<div :class="tipStyle" slot="content">
</template>
<div :class="tipStyle">
<div class="words">强度 : {{ strength }}</div>
<Progress
:percent="strengthValue"
<el-progress
:percentage="strengthValue"
:status="progressStatus"
hide-info
style="margin: 13px 0;"
:show-text="false"
style="margin: 13px 0"
/>
<br />请至少输入 6 个字符请不要使
<br />用容易被猜到的密码
<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 {
.password-tip-weak .words {
color: #ed3f14;
}
}
.password-tip-middle {
padding: 1vh 0;
.words {
.password-tip-middle .words {
color: #2d8cf0;
}
}
.password-tip-strong {
padding: 1vh 0;
.words {
.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>-->
</div>
<Modal
title="图片预览"
v-model="viewImage"
:styles="{ top: '30px' }"
:z-index="3500"
draggable
<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"
>
<img :src="currentValue" alt="该资源不存在" style="max-width: 300px;margin: 0 auto;display: block;" />
<div slot="footer">
<Button @click="viewImage=false">关闭</Button>
查看大图
</el-button>
</el-popover>
</template>
</el-input>
<el-button @click="handleCLickImg('storeLogo')">选择图片</el-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

@@ -9,181 +9,163 @@
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,55 +173,44 @@ 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);
});
this.$emit("on-change", v);
}
list.forEach((e) => {
this.uploadList.push({
status: "finished",
...(typeof e === "string" ? { url: e } : e),
});
});
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>
@@ -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 = []
if (level == "city") {
this.data.area = [];
this.data.street = [];
this.chiosend.splice(1, 3, "", "", "");
}
if (level == 'area') {
this.data.street = []
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
this.$Message.error("请选择地址");
return;
}
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,140 +1,123 @@
<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 {
props: ["addressId"],
data() {
return {
data: [], // 地区数据
addr: [] // 已选数据
data: [],
addr: [],
};
},
props: ['addressId'],
mounted () {},
methods: {
change (val, selectedData) { // 选择地区
/**
* @returns [regionId,region]
*/
this.$emit('selected', [
val,
selectedData[selectedData.length - 1].__label.split('/')
]);
computed: {
cascaderProps() {
return {
value: "value",
label: "label",
lazy: true,
lazyLoad: this.loadData,
};
},
loadData (item, callback) { // 加载数据
item.loading = true;
getChildRegion(item.value).then((res) => {
if (res.result.length <= 0) {
item.loading = false;
} else {
res.result.forEach((child) => {
item.loading = false;
let data = {
},
methods: {
change(val) {
if (!val || !val.length) {
this.$emit("selected", [[], []]);
return;
}
const labels = this.resolveLabels(val);
this.$emit("selected", [val, labels]);
},
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,
loading: false,
children: []
leaf: isLeaf,
};
if (child.level === 'street' || item.label === '香港特别行政区') {
item.children.push({
value: child.id,
label: child.name
});
} else {
item.children.push(data);
}
});
callback();
}
resolve(nodes);
});
},
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 = {
async init() {
const data = await getChildRegion(0);
this.data = data.result.map((item) => ({
value: item.id,
label: item.name,
loading: false,
children: []
};
}
arr.push(obj);
});
this.data = arr;
leaf: item.name === "台湾省",
}));
},
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 = {
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,
loading: false,
children: []
};
}
arr0.push(obj);
});
// 根据选择的数据来加载数据列表
leaf: item.name === "台湾省",
children: [],
}));
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;
}
});
}
@@ -142,41 +125,25 @@ export default {
this.addr = addr;
},
handleData(data) {
// 处理接口数据
let item = [];
data.forEach((child) => {
let obj = {
return data.map((child) => ({
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;
}
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="bgcolor"
:style="{ width: distance + 'px', background: bgColor }"
></span>
<span class="swiper" :style="{ left: distance + 'px' }" @mousedown="mouseDown">
<Icon type="md-arrow-round-forward" />
<el-icon><DArrowRight /></el-icon>
</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() {
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) {
this.downX = e.clientX;
this.flag = true;
},
// 鼠标移动事件,计算距离
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,63 +85,61 @@ export default {
}
}
},
// 鼠标抬起事件,验证是否正确
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 => {
postVerifyImg(params)
.then((res) => {
if (res.success) {
if (res.result) {
this.bgColor = 'green';
this.verifyText = '解锁成功';
this.$emit('change', { status: true, distance: this.distance });
this.bgColor = "green";
this.verifyText = "解锁成功";
this.$emit("change", { status: true, distance: this.distance });
} else {
this.bgColor = 'red';
this.verifyText = '解锁失败';
let that = this;
setTimeout(() => {
that.init();
}, 1000);
this.$emit('change', { status: false, distance: this.distance });
this.bgColor = "red";
this.verifyText = "解锁失败";
setTimeout(() => this.init(), 1000);
this.$emit("change", { status: false, distance: this.distance });
}
} else {
this.init()
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 {
padding: 10px;
@@ -174,10 +194,8 @@ export default {
display: flex;
align-items: center;
justify-content: center;
.ivu-icon {
font-size: 20px;
}
}
.text {
display: inline-block;

View File

@@ -1,7 +1,7 @@
import axios from "axios";
import { getStore, setStore } from "./storage.js";
import { router } from "../router/index";
import { Message } from "view-design";
import { Message } from "@/utils/message";
import Cookies from "js-cookie";
import { handleRefreshToken } from "../api/index";
import {v4 as uuidv4} from 'uuid';

View File

@@ -1,10 +1,130 @@
import { getCurrentPermissionList } from "@/api/index";
import lazyLoading from "./lazyLoading.js";
import { router } from "@/router/index";
import Cookies from "js-cookie";
let util = {};
/** 动态菜单路由 name用于登出或重新注册时清理 */
util.dynamicRouteNames = [];
util.clearDynamicRoutes = function () {
util.dynamicRouteNames.forEach((name) => {
if (router.hasRoute(name)) {
router.removeRoute(name);
}
});
util.dynamicRouteNames = [];
};
util.collectLeafRoutes = function (routes, result = [], parentPath = "") {
routes.forEach((route) => {
let segment = route.path != null ? String(route.path) : "";
segment = segment.replace(/^\//, "");
const fullPath = [parentPath, segment].filter(Boolean).join("/");
const hasChildren = route.children && route.children.length > 0;
if (hasChildren) {
util.collectLeafRoutes(route.children, result, fullPath);
} else if (
route.name &&
route.component &&
!String(route.name).endsWith("__layout")
) {
result.push({
path: fullPath || segment || route.name,
name: route.name,
component: route.component,
meta: route.meta || {},
});
}
});
return result;
};
/** Vue Router 4动态路由必须挂到 otherRouter 下,且刷新/热更新后需重新注册 */
util.registerDynamicRoutes = function (menuData, options = {}) {
const { rematch = true } = options;
const pendingPath = router.currentRoute.value.fullPath;
const pendingUnmatched = router.currentRoute.value.matched.length === 0;
util.clearDynamicRoutes();
const constRoutes = [];
util.initAllMenuData(constRoutes, menuData);
const leaves = [];
constRoutes.forEach((top) => {
const base = (top.path || "").replace(/^\//, "");
if (top.children && top.children.length) {
util.collectLeafRoutes(top.children, leaves, base);
} else if (
top.name &&
top.component &&
!String(top.name).endsWith("__layout")
) {
leaves.push({
path: base || top.name,
name: top.name,
component: top.component,
meta: top.meta || {},
});
}
});
leaves.forEach((leaf) => {
const path = (leaf.path || leaf.name || "").replace(/^\//, "");
if (!router.hasRoute(leaf.name)) {
router.addRoute("otherRouter", {
path,
name: leaf.name,
component: leaf.component,
meta: leaf.meta,
});
util.dynamicRouteNames.push(leaf.name);
}
});
if (!router.hasRoute("error-404")) {
router.addRoute({
path: "/:pathMatch(.*)*",
name: "error-404",
component: lazyLoading("error-page/404"),
meta: { title: "404-页面不存在" },
});
util.dynamicRouteNames.push("error-404");
}
if (
rematch &&
pendingUnmatched &&
pendingPath &&
pendingPath !== "/login"
) {
const resolved = router.resolve(pendingPath);
if (resolved.matched.length > 0) {
router.replace(pendingPath).catch(() => {});
}
}
};
/** 从 localStorage 恢复动态路由(刷新深链前必须先注册,避免 /member/xxx 无匹配) */
util.bootstrapDynamicRoutesFromCache = function () {
if (!Cookies.get("userInfoManager")) {
return false;
}
try {
const raw = window.localStorage.getItem("menuData");
if (!raw) {
return false;
}
util.registerDynamicRoutes(JSON.parse(raw), { rematch: false });
return true;
} catch (e) {
console.warn("[router] menuData parse failed", e);
return false;
}
};
util.title = function(title) {
title = title || "运营后台";
window.document.title = title;
@@ -94,7 +214,7 @@ util.initRouter = function(vm) {
// 404路由需要和动态路由一起加载
const otherRouter = [
{
path: "/*",
path: "/:pathMatch(.*)*",
name: "error-404",
meta: {
title: "404-页面不存在"
@@ -132,15 +252,11 @@ util.initRouter = function(vm) {
return;
}
util.initAllMenuData(constRoutes, menuData);
util.initRouterNode(otherRoutes, otherRouter);
// 添加所有主界面路由
util.registerDynamicRoutes(menuData);
vm.$store.commit(
"updateAppRouter",
constRoutes.filter(item => item.children.length > 0)
constRoutes.filter((item) => item.children && item.children.length > 0)
);
// 添加全局路由
vm.$store.commit("updateDefaultRouter", otherRoutes);
// 添加菜单路由
util.initMenuData(vm, menuData);
// 缓存数据 修改加载标识
window.localStorage.setItem("menuData", JSON.stringify(menuData));
@@ -154,7 +270,7 @@ util.initRouter = function(vm) {
return;
}
let menuData = JSON.parse(data);
// 添加菜单路由
util.registerDynamicRoutes(menuData);
util.initMenuData(vm, menuData);
}
};
@@ -235,17 +351,27 @@ util.initRouterNode = function(routers, data) {
// data为所有子菜单数据
for (let item of data) {
let menu = Object.assign({}, item);
const menu = Object.assign({}, item);
const hasChildren = item.children && item.children.length > 0;
menu.component = lazyLoading(menu.frontRoute);
if (item.children && item.children.length > 0) {
if (hasChildren) {
menu.children = [];
// Vue Router 4父子路由 name 必须不同,后端菜单常出现同名父子节点
if (menu.name) {
menu.name = `${menu.name}__layout`;
}
util.initRouterNode(menu.children, item.children);
// 分组节点通常无页面,仅在有 frontRoute 时挂载组件
if (menu.frontRoute) {
menu.component = lazyLoading(menu.frontRoute);
} else {
delete menu.component;
}
} else if (menu.frontRoute) {
menu.component = lazyLoading(menu.frontRoute);
}
let meta = {};
// 给页面添加标题、父级菜单name方便左侧菜单选中
const meta = {};
meta.title = menu.title ? menu.title + " - 运营后台" : null;
meta.firstRouterName = item.firstRouterName;
menu.meta = meta;

View File

@@ -1,28 +1,22 @@
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import zhLocale from './lang/zh-CN';
import enLocale from './lang/en-US';
import zhCnLocale from 'view-design/src/locale/lang/zh-CN';
import enUsLocale from 'view-design/src/locale/lang/en-US';
import { createI18n } from "vue-i18n";
import zhLocale from "./lang/zh-CN";
import enLocale from "./lang/en-US";
Vue.use(VueI18n);
// 根据浏览器信息自动设置语言
const navLang = navigator.language;
const localLang = (navLang == 'zh-CN' || navLang == 'en-US') ? navLang : false;
const lang = window.localStorage.lang || localLang || 'zh-CN';
const localLang = navLang === "zh-CN" || navLang === "en-US" ? navLang : false;
const lang = window.localStorage.lang || localLang || "zh-CN";
Vue.config.lang = lang;
// 多语言配置 vue-i18n 6.x+
Vue.locale = () => { };
const messages = {
'zh-CN': Object.assign(zhCnLocale, zhLocale),
'en-US': Object.assign(enUsLocale, enLocale)
"zh-CN": zhLocale,
"en-US": enLocale,
};
const i18n = new VueI18n({
const i18n = createI18n({
legacy: true,
globalInjection: true,
locale: lang,
messages
fallbackLocale: "zh-CN",
messages,
});
export default i18n;

View File

@@ -1,114 +1,88 @@
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import ViewUI from 'view-design'
// import 'view-design/dist/styles/iview.css'
import './styles/theme.less';
import { createApp } from "vue";
import "core-js/stable";
import "./styles/theme.less";
import App from "./App.vue";
import { router } from "./router/index";
import store from "./store";
import i18n from "@/locale";
import { setupElementPlus } from "@/plugins/element";
import { setupLegacyMessage } from "@/utils/message";
import liliDialog from "@/components/lili-dialog";
import PriceColorScheme from "@/components/price-color-scheme.vue";
import { install as installVueQr } from "vue-qr";
import {
getRequest,
postRequest,
putRequest,
deleteRequest,
importRequest,
uploadFileRequest,
} from "@/libs/axios";
import { setStore, getStore, removeStore } from "@/libs/storage";
import util from "@/libs/util";
import { md5 } from "@/utils/md5.js";
import * as filters from "@/utils/filters";
import "core-js/stable"
// import "regenerator-runtime/runtime"
import App from './App'
import {router} from './router/index'
import store from './store'
import i18n from '@/locale'
const { aMapSecurityJsCode, mainColor } = require("@/config");
import {getRequest, postRequest, putRequest, deleteRequest, importRequest, uploadFileRequest} from '@/libs/axios'
import {setStore, getStore, removeStore} from '@/libs/storage'
import util from '@/libs/util'
import * as filters from '@/utils/filters' // global filter
import liliDialog from '@/components/lili-dialog'
import {md5} from '@/utils/md5.js';
// 打印
import Print from 'vue-print-nb';
Vue.use(Print);
const {aMapSecurityJsCode, inputMaxLength,mainColor } = require("@/config");
// 高德安全密钥
if (aMapSecurityJsCode) {
window._AMapSecurityConfig = {
securityJsCode: aMapSecurityJsCode,
};
}
const PC_URL = BASE.PC_URL;
const WAP_URL = BASE.WAP_URL;
Vue.config.devtools = true;
Vue.config.productionTip = false
const PC_URL = BASE.PC_URL; // 跳转买家端地址 pc端
const WAP_URL = BASE.WAP_URL; // 跳转买家端地址 wap端
Vue.prototype.linkTo = function (goodsId, skuId) {
// 跳转买家端商品
// 刷新深链(如 /member/user-manage前先从缓存注册动态路由
util.bootstrapDynamicRoutesFromCache();
const app = createApp(App);
setupElementPlus(app);
setupLegacyMessage(app);
installVueQr(app);
app.use(router);
app.use(store);
app.use(i18n);
app.component("liliDialog", liliDialog);
app.component("priceColorScheme", PriceColorScheme);
app.config.globalProperties.getRequest = getRequest;
app.config.globalProperties.postRequest = postRequest;
app.config.globalProperties.putRequest = putRequest;
app.config.globalProperties.deleteRequest = deleteRequest;
app.config.globalProperties.importRequest = importRequest;
app.config.globalProperties.uploadFileRequest = uploadFileRequest;
app.config.globalProperties.setStore = setStore;
app.config.globalProperties.getStore = getStore;
app.config.globalProperties.removeStore = removeStore;
app.config.globalProperties.$mainColor = mainColor;
app.config.globalProperties.md5 = md5;
app.config.globalProperties.$filters = filters;
app.config.globalProperties.linkTo = function (goodsId, skuId) {
let src;
if (skuId) {
src = `${PC_URL}/goodsDetail?skuId=${skuId}&goodsId=${goodsId}`;
} else {
src = `${PC_URL}/goodsDetail?goodsId=${goodsId}`;
}
window.open(src, "_blank");
};
Vue.prototype.wapLinkTo = function (goodsId, skuId) {
// app端二维码
app.config.globalProperties.wapLinkTo = function (goodsId, skuId) {
if (skuId) {
return `${WAP_URL}/pages/product/goods?id=${skuId}&goodsId=${goodsId}`;
} else {
return `${WAP_URL}/pages/product/goods?goodsId=${goodsId}`;
}
return `${WAP_URL}/pages/product/goods?goodsId=${goodsId}`;
};
// 引入价格格式化组件
import priceColorScheme from 'price-color'
Vue.use(priceColorScheme);
const copyViewUi = {...ViewUI}
copyViewUi.Input.props.maxlength.default = inputMaxLength // 挂载最大输入值
Vue.use(copyViewUi, {
i18n: (key, value) => i18n.t(key, value),
router.isReady().then(() => {
app.mount("#app");
});
Vue.component('liliDialog', liliDialog)
// 挂载全局使用的方法
Vue.prototype.getRequest = getRequest;
Vue.prototype.postRequest = postRequest;
Vue.prototype.putRequest = putRequest;
Vue.prototype.deleteRequest = deleteRequest;
Vue.prototype.importRequest = importRequest;
Vue.prototype.uploadFileRequest = uploadFileRequest;
Vue.prototype.setStore = setStore;
Vue.prototype.getStore = getStore;
Vue.prototype.removeStore = removeStore;
Vue.prototype.$mainColor = mainColor;
Vue.prototype.md5 = md5;
Array.prototype.remove = function (from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
i18n,
render: h => h(App),
data: {
currentPageName: ''
},
mounted() {
// 初始化菜单
util.initRouter(this);
this.currentPageName = this.$route.name;
// 显示打开的页面的列表
this.$store.commit('setOpenedList');
this.$store.commit('initCachePage');
}
})
// 登录后由 App.vue 触发 initRouter 拉取/更新菜单
export { app, util };

View File

@@ -0,0 +1,11 @@
import ElementPlus from "element-plus";
import zhCn from "element-plus/es/locale/lang/zh-cn";
import "element-plus/dist/index.css";
import "@/styles/element.scss";
export function setupElementPlus(app) {
app.use(ElementPlus, {
locale: zhCn,
size: "default",
});
}

View File

@@ -1,56 +1,46 @@
import Vue from 'vue';
import ViewUI from 'view-design';
import Util from '../libs/util';
import VueRouter from 'vue-router';
import Cookies from 'js-cookie';
import {routers} from './router';
import { createRouter, createWebHistory } from "vue-router";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import Util from "../libs/util";
import Cookies from "js-cookie";
import store from "@/store";
import { routers } from "./router";
Vue.use(VueRouter);
NProgress.configure({ showSpinner: false });
// 路由配置
const RouterConfig = {
mode: 'history',
routes: routers
};
/**
* 解决重复点击菜单会控制台报错bug
*/
const routerPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return routerPush.call(this, location).catch(error=> error)
}
export const router = new VueRouter(RouterConfig);
export const router = createRouter({
history: createWebHistory(),
routes: routers,
});
router.beforeEach((to, from, next) => {
ViewUI.LoadingBar.start();
NProgress.start();
Util.title(to.meta.title);
next();
const name = to.name;
const hasToken = Cookies.get("userInfoManager");
if (!Cookies.get('userInfoManager') && name !== 'login') {
// 判断是否已经登录且前往的页面不是登录页
next({
name: 'login'
});
} else if (Cookies.get('userInfoManager') && name === 'login') {
// 判断是否已经登录且前往的是登录页
Util.title();
next({
name: 'home_index'
});
} else {
Util.toDefaultPage([...routers], name, router, next);
if (!hasToken && name !== "login") {
next({ name: "login" });
return;
}
if (hasToken && name === "login") {
Util.title();
next({ name: "home_index" });
return;
}
if (hasToken) {
Util.toDefaultPage([...routers], name, router, next);
return;
}
next();
});
router.afterEach((to) => {
Util.openNewPage(router.app, to.name, to.params, to.query);
ViewUI.LoadingBar.finish();
Util.openNewPage({ $store: store }, to.name, to.params, to.query);
NProgress.done();
window.scrollTo(0, 0);
});

View File

@@ -1,30 +1,23 @@
import Vue from "vue";
import Vuex from "vuex";
import { createStore } from "vuex";
import app from "./modules/app";
import setting from './modules/setting';
import setting from "./modules/setting";
import user from "./modules/user";
import dict from "./modules/dict";
Vue.use(Vuex);
const store = new Vuex.Store({
const store = createStore({
state: {
// 状态
openStyleStore: "", // 移动端楼层装修中选择风格存储
openStyleStore: "",
openStoreId: "",
notices: "" //通知提示信息
},
mutations: {
// 改变方法
notices: "",
},
mutations: {},
actions: {},
modules: {
app,
user,
setting,
dict
}
dict,
},
});
export default store;

View File

@@ -5,7 +5,7 @@ import {
router
} from '@/router/index';
import Util from '@/libs/util';
import Vue from 'vue';
import i18n from '@/locale';
const app = {
state: {
@@ -34,11 +34,10 @@ const app = {
// 动态添加主界面路由,需要缓存
updateAppRouter(state, routes) {
state.routers.push(...routes);
router.addRoutes(routes);
// 实际 addRoute 在 util.registerDynamicRoutes 中完成
},
// 动态添加全局路由404、500等页面不需要缓存
updateDefaultRouter(state, routes) {
router.addRoutes(routes);
// 兼容旧调用404 由 registerDynamicRoutes 注册
},
setAdded(state, v) {
state.added = v;
@@ -122,7 +121,7 @@ const app = {
switchLang(state, lang) {
state.lang = lang;
localStorage.lang = lang;
Vue.config.lang = lang;
i18n.global.locale = lang;
},
setMessageCount(state, count) {
state.messageCount = count;

View File

@@ -1,11 +1,12 @@
import Cookies from 'js-cookie';
import util from '@/libs/util';
const user = {
state: {},
mutations: {
logout () {
util.clearDynamicRoutes();
Cookies.remove('userInfoManager');
// 清空打开的页面等数据
localStorage.clear();
}
}

View File

@@ -124,5 +124,50 @@ $bg_color: #f1f6fa;
}
// table-common原 @import 合并,避免 Sass @import 弃用警告)
.search {
.operation {
margin-bottom: 2vh;
}
.select-clear {
margin-left: 10px;
}
}
.search-input {
width: 270px;
margin-right: 20px;
}
.search-form {
width: 100% !important;
display: flex;
align-items: center;
border-radius: 0.4em;
flex-wrap: wrap;
@import "./table-common.scss";
> .ivu-form-item,
> .el-form-item {
margin: 8px 10px !important;
}
}
.padding-row {
margin-top: 15px;
margin-bottom: 15px;
}
.search-btn {
margin-left: 20px;
}
.div-zoom {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 4;
overflow: hidden;
}
// 列表页「筛选 Card + 列表 Card」间距Vue3 使用 el-card
.search > .el-card + .el-card {
margin-top: 10px;
}
// 兼容遗留 iView 结构
.ivu-card + .ivu-card {
margin-top: 10px;
}

View File

@@ -0,0 +1,97 @@
/* Element Plus 主题覆盖(对齐原 iView 主色) */
:root {
--el-color-primary: #ff5c58;
--el-color-success: #68cabe;
--el-color-warning: #fa6419;
--el-color-danger: #ff3c2a;
--el-font-size-extra-small: 12px;
--el-font-size-small: 13px;
--el-font-size-base: 14px;
--el-font-size-large: 16px;
}
.el-button--primary {
--el-button-bg-color: #ff5c58;
--el-button-border-color: #ff5c58;
--el-button-hover-bg-color: #ff7875;
--el-button-hover-border-color: #ff7875;
}
/* 全局表格:去掉列竖线,保留行横线 */
.el-table--border {
.el-table__cell {
border-right: none !important;
}
.el-table__border-left-patch {
display: none;
}
&::before,
&::after,
.el-table__inner-wrapper::before,
.el-table__inner-wrapper::after {
width: 0 !important;
}
&.el-table--group::before,
&.el-table--group::after,
&.el-table--group .el-table__inner-wrapper::before,
&.el-table--group .el-table__inner-wrapper::after {
width: 0 !important;
}
}
.el-table__fixed-right::before,
.el-table__fixed::before {
background-color: transparent;
}
/* 列表操作列统一样式 */
.link-text {
color: #409eff;
cursor: pointer;
text-decoration: none;
&:hover {
color: #66b1ff;
}
&.disabled {
color: #c0c4cc;
cursor: not-allowed;
pointer-events: none;
}
}
.op-split {
margin: 0 8px;
color: #dcdfe6;
}
.el-table .ops {
display: inline-flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
a:not(.link-text) {
color: #409eff;
cursor: pointer;
text-decoration: none;
&:hover {
color: #66b1ff;
}
}
> span:not(.op-split) {
margin: 0 8px;
color: #dcdfe6;
}
}
.gcc-disabled-action {
color: #c0c4cc;
cursor: not-allowed;
}

View File

@@ -23,7 +23,8 @@
border-radius: 0.4em;
flex-wrap: wrap;
> .ivu-form-item {
> .ivu-form-item,
> .el-form-item {
margin: 8px 10px !important;
}
}
@@ -42,7 +43,12 @@
overflow: hidden;
}
// 为Card组件之间增加间距
// 列表页相邻 Card 间距Element Plus
.search > .el-card + .el-card {
margin-top: 10px;
}
// 兼容遗留 iView
.ivu-card + .ivu-card {
margin-top: 10px;
}

View File

@@ -1,6 +1,4 @@
@import "~view-design/src/styles/index.less";
// iview 自定义样式
// Element Plus 主题变量(原 view-design less 已移除)
@primary-color: #ff5c58;
@info-color: #fa6419;
@success-color: #68cabe;
@@ -9,11 +7,9 @@
@table-td-stripe-bg: #f8f8f9;
@table-td-hover-bg: #ededed;
@table-td-highlight-bg: #ededed;
@font-size-base: 12px;
@font-size-base: 14px;
.ivu-drawer,
.drawer,
.ivu-drawer-wrap {
.el-drawer,
.drawer {
z-index: 2600 !important;
}

View File

@@ -27,8 +27,8 @@
background: #e4e4e4;
}
.block-tool .ivu-tooltip,
.block-tool .ivu-tooltip-rel {
.block-tool .el-tooltip,
.block-tool .el-tooltip__trigger {
display: block;
}

View File

@@ -0,0 +1,74 @@
import { ElMessage, ElMessageBox } from "element-plus";
/**
* 兼容原 view-design Message API便于业务页渐进迁移
*/
export const Message = {
success(content) {
return ElMessage.success(normalize(content));
},
error(content) {
return ElMessage.error(normalize(content));
},
warning(content) {
return ElMessage.warning(normalize(content));
},
info(content) {
return ElMessage.info(normalize(content));
},
};
export const Notice = {
open(options = {}) {
const fn = Message[options.type] || Message.info;
return fn(options.desc || options.title || "");
},
info(options) {
return Message.info(options?.desc || options?.title || "");
},
success(options) {
return Message.success(options?.desc || options?.title || "");
},
warning(options) {
return Message.warning(options?.desc || options?.title || "");
},
error(options) {
return Message.error(options?.desc || options?.title || "");
},
};
export const Modal = {
confirm(options = {}) {
const content = options.content || options.title || "确认操作?";
return ElMessageBox.confirm(content, options.title || "提示", {
confirmButtonText: options.okText || "确定",
cancelButtonText: options.cancelText || "取消",
type: options.type || "warning",
})
.then(() => {
if (typeof options.onOk === "function") {
return options.onOk();
}
})
.catch(() => {
if (typeof options.onCancel === "function") {
options.onCancel();
}
});
},
remove() {
ElMessageBox.close();
},
};
function normalize(content) {
if (typeof content === "string") return content;
if (content && content.content) return content.content;
return String(content ?? "");
}
export function setupLegacyMessage(app) {
app.config.globalProperties.$Message = Message;
app.config.globalProperties.$Modal = Modal;
app.config.globalProperties.$Notice = Notice;
}

View File

@@ -3,155 +3,126 @@
</style>
<template>
<div class="main">
<!-- 左侧菜单 -->
<div class="sidebar-menu-con menu-bar">
<div class="logo-con">
<!-- <img src="../assets/logo.png" key="max-logo" /> -->
<shrinkable-menu />
</div>
<div class="main-header-con" :style="{ height: '60px' }">
<div class="main-header">
<div class="header-logo-con">
<img :src="domainLogo" key="max-logo" />
</div>
<shrinkable-menu></shrinkable-menu>
</div>
<!-- 顶部标题栏主体 -->
<div class="main-header-con" :style="{ height: setting.isUseTabsRouter ? '100px' : '60px' }">
<div class="main-header">
<div class="header-avator-con">
<!-- 左侧栏 -->
<div>
</div>
<div class="flex flex-a-c user-module">
<!-- 通知消息 -->
<message-tip v-if="tipsMessage" :res="tipsMessage"></message-tip>
<!-- 用户头像 -->
<div class="user-dropdown-menu-con">
<Row
<div class="flex flex-a-c user-module" style="height: 100%">
<message-tip v-if="tipsMessage" :res="tipsMessage" />
<div class="user-dropdown-menu-con" style="margin-left: 24px; display: flex; align-items: center; height: 100%">
<el-row
type="flex"
justify="end"
align="middle"
class="user-dropdown-innercon"
>
<ul class="nav-list">
<li class="nav-item " @click="handleClickSetting">
<Tooltip content="设置">
<Icon size="16" type="md-settings" />
</Tooltip>
</li>
</ul>
<Dropdown
transfer
trigger="hover"
@on-click="handleClickUserDropdown"
>
<el-dropdown trigger="hover" @command="handleClickUserDropdown">
<div class="dropList">
<span class="main-user-name">{{ userInfo.nickName }}</span>
<Avatar
icon="ios-person"
<el-avatar
:size="32"
:src="avatarPath"
style="background: #fff; margin-left: 10px"
></Avatar>
>
<el-icon><UserFilled /></el-icon>
</el-avatar>
</div>
<DropdownMenu slot="list">
<DropdownItem name="personalCenter">{{
$t("userCenter")
}}</DropdownItem>
<DropdownItem name="changePass">{{
$t("changePass")
}}</DropdownItem>
<DropdownItem name="loginOut" divided>{{
$t("logout")
}}</DropdownItem>
</DropdownMenu>
</Dropdown>
</Row>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="personalCenter">
{{ $t("userCenter") }}
</el-dropdown-item>
<el-dropdown-item command="changePass">
{{ $t("changePass") }}
</el-dropdown-item>
<el-dropdown-item divided command="loginOut">
{{ $t("logout") }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-row>
</div>
</div>
</div>
</div>
<!-- 已打开的页面标签 -->
<div class="tags-con" v-if="setting.isUseTabsRouter">
<tags-page-opened :pageTagsList="pageTagsList"></tags-page-opened>
</div>
<div class="breadcrumb-bar" v-if="setting.isUseTabsRouter">
<page-breadcrumb />
</div>
<div class="single-page-con" :style="{ 'top': setting.isUseTabsRouter ? '100px' : '60px', height: setting.isUseTabsRouter ? 'calc(100% - 110px)' : 'calc(100% - 70px)' }">
<div
class="single-page-con"
:style="{
top: setting.isUseTabsRouter ? '104px' : '60px',
height: setting.isUseTabsRouter ? 'calc(100% - 114px)' : 'calc(100% - 70px)',
}"
>
<div class="single-page">
<!-- <keep-alive :include="cachePage"> -->
<router-view></router-view>
<!-- </keep-alive> -->
<router-view v-slot="{ Component }">
<component :is="Component" v-if="Component" :key="$route.fullPath" />
</router-view>
</div>
</div>
<!-- 全局加载动画 -->
<circleLoading class="loading-position" v-show="loading" />
<!-- 右侧抽屉配置 -->
<configDrawer ref="config"/>
</div>
</template>
<script>
import { UserFilled } from "@element-plus/icons-vue";
import shrinkableMenu from "./main-parts/shrinkable-menu/shrinkable-menu.vue";
import tagsPageOpened from "./main-parts/tags-page-opened.vue";
import pageBreadcrumb from "./main-parts/page-breadcrumb.vue";
import messageTip from "./main-parts/message-tip.vue";
import circleLoading from "@/components/lili/circle-loading.vue";
import configDrawer from "@/components/lili/config-drawer.vue";
import Cookies from "js-cookie";
import util from "@/libs/util.js";
import { getNoticePage, logout } from "@/api/index";
import { getBaseSite } from "@/api/common";
var client;
const config = require("@/config/index.js");
export default {
components: {
UserFilled,
shrinkableMenu,
tagsPageOpened,
pageBreadcrumb,
messageTip,
circleLoading,
configDrawer
},
data() {
return {
config,
sliceNum: 5, // 展示nav数量
userInfo: "", // 用户信息
tipsMessage: "", // 通知消息
sliceNum: 5,
userInfo: "",
tipsMessage: "",
defaultLogo: require("@/assets/logo.png"),
domainLogo: "",
};
},
computed: {
setting() {
let data = this.$store.state.setting
return data.setting
return this.$store.state.setting.setting;
},
loading() {
return this.$store.state.app.loading;
},
pageTagsList() {
return this.$store.state.app.pageOpenedList; // 打开的页面的页面对象
return this.$store.state.app.pageOpenedList;
},
avatarPath() {
return localStorage.avatorImgPath;
},
lang() {
return this.$store.state.app.lang;
},
},
methods: {
handleClickSetting() {
this.$refs.config.open();
},
init() {
// 菜单初始化
let userInfo = JSON.parse(Cookies.get("userInfoManager"));
const userInfo = JSON.parse(Cookies.get("userInfoManager"));
this.userInfo = userInfo;
this.checkTag(this.$route.name);
let currWidth = document.body.clientWidth;
if (currWidth <= 1200) {
this.sliceNum = 2;
}
this.domainLogo = localStorage.getItem("icon");
let link =
this.domainLogo = localStorage.getItem("icon") || this.defaultLogo;
const link =
document.querySelector("link[rel*='icon']") ||
document.createElement("link");
link.type = "image/x-icon";
@@ -159,35 +130,22 @@ export default {
link.rel = "shortcut icon";
document.getElementsByTagName("head")[0].appendChild(link);
window.document.title = localStorage.getItem("title") + " - 运营后台";
// 读取未读消息数
getNoticePage({}).then((res) => {
if (res.success) {
this.tipsMessage = res.result;
this.$store.state.notices = res.result;
}
});
},
//用户头像下方抽屉点击
handleClickUserDropdown(name) {
//个人中心
if (name === "personalCenter") {
util.openNewPage(this, "personal-center");
this.$router.push({
name: "personal-center",
});
}
//修改密码
else if (name === "changePass") {
this.$router.push({ name: "personal-center" });
} else if (name === "changePass") {
util.openNewPage(this, "change-password");
this.$router.push({
name: "change_password",
});
}
// 退出登录
else if (name === "loginOut") {
logout().then((res) => {
this.$router.push({ name: "change_password" });
} else if (name === "loginOut") {
logout().then(() => {
this.$store.commit("logout", this);
this.$store.commit("setAdded", false);
this.setStore("accessToken", "");
@@ -196,15 +154,9 @@ export default {
});
}
},
//切换标签
checkTag(name) {
let openpageHasTag = this.pageTagsList.some((item) => {
if (item.name == name) {
return true;
}
});
const openpageHasTag = this.pageTagsList.some((item) => item.name == name);
if (!openpageHasTag) {
// 解决关闭当前标签后再点击回退按钮会退到当前页时没有标签的问题
util.openNewPage(
this,
name,
@@ -213,33 +165,33 @@ export default {
);
}
},
//宽度动态计算
resize() {
let currWidth = document.body.clientWidth;
let count = currWidth / 300;
if (count > 6) {
this.sliceNum = 6;
} else {
this.sliceNum = count;
const currWidth = document.body.clientWidth;
const count = currWidth / 300;
this.sliceNum = count > 6 ? 6 : count;
},
onLogoError(e) {
if (e?.target && e.target.src !== this.defaultLogo) {
e.target.src = this.defaultLogo;
}
},
},
watch: {
$route(to, from) {
$route(to) {
this.checkTag(to.name);
localStorage.currentPageName = to.name;
},
},
mounted() {
this.init();
let that = this;
this.resize();
window.addEventListener("resize", function () {
that.resize();
});
window.addEventListener("resize", this.resize);
this.$store.commit("setOpenedList");
},
beforeUnmount() {
window.removeEventListener("resize", this.resize);
},
created() {
// 显示打开的页面的列表
this.$store.commit("setOpenedList");
},
};

View File

@@ -1,37 +1,53 @@
<template>
<div>
<Card class="change-pass">
<p slot="title">修改密码</p>
<div class="mt_10">
<Form ref="editPasswordForm" :model="editPasswordForm" :label-width="100" label-position="right" :rules="passwordValidate" style="width:450px">
<FormItem label="原密码" prop="oldPass">
<Input type="password" v-model="editPasswordForm.oldPassword" placeholder="请输入现在使用的密码"></Input>
</FormItem>
<FormItem label="新密码" prop="newPass">
<el-card class="change-pass">
<template #header>修改密码</template>
<el-form
ref="editPasswordForm"
:model="editPasswordForm"
label-width="100px"
label-position="right"
:rules="passwordValidate"
style="width: 450px"
class="mt_10"
>
<el-form-item label="原密码" prop="oldPassword">
<el-input
v-model="editPasswordForm.oldPassword"
type="password"
show-password
placeholder="请输入现在使用的密码"
/>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<SetPassword v-model="editPasswordForm.newPassword" @on-change="changeInputPass" />
</FormItem>
<FormItem label="确认新密码" prop="rePass">
<Input type="password" v-model="editPasswordForm.rePassword" placeholder="请再次输入新密码"></Input>
</FormItem>
<FormItem>
<Button type="primary" class="mr_10" :loading="savePassLoading" @click="saveEditPass">保存
</Button>
<Button @click="cancelEditPass">取消</Button>
</FormItem>
</Form>
</div>
</Card>
</el-form-item>
<el-form-item label="确认新密码" prop="rePassword">
<el-input
v-model="editPasswordForm.rePassword"
type="password"
show-password
placeholder="请再次输入新密码"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="savePassLoading" @click="saveEditPass">保存</el-button>
<el-button @click="cancelEditPass">取消</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
import SetPassword from "@/components/lili/set-password";
import { changePass } from "@/api/index";
import { ElMessageBox } from "element-plus";
export default {
name: "change-password",
components: {
SetPassword
SetPassword,
},
data() {
const valideRePassword = (rule, value, callback) => {
@@ -42,102 +58,61 @@ export default {
}
};
return {
savePassLoading: false, // 保存加载状态
editPasswordForm: { // 编辑密码表单
savePassLoading: false,
editPasswordForm: {
oldPassword: "",
newPassword: "",
rePassword: "",
},
strength: "", // 密码强度
passwordValidate: { // 验证规则
oldPassword: [
{
required: true,
message: "请输入原密码",
trigger: "blur",
},
],
strength: "",
passwordValidate: {
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
newPassword: [
{
required: true,
message: "请输入新密码",
trigger: "blur",
},
{
min: 6,
message: "请至少输入6个字符",
trigger: "blur",
},
{
max: 32,
message: "最多输入32个字符",
trigger: "blur",
},
{ required: true, message: "请输入新密码", trigger: "blur" },
{ min: 6, message: "请至少输入6个字符", trigger: "blur" },
{ max: 32, message: "最多输入32个字符", trigger: "blur" },
],
rePassword: [
{
required: true,
message: "请再次输入新密码",
trigger: "blur",
},
{
validator: valideRePassword,
trigger: "blur",
},
{ required: true, message: "请再次输入新密码", trigger: "blur" },
{ validator: valideRePassword, trigger: "blur" },
],
},
};
},
methods: {
// 密码强度
changeInputPass(v, grade, strength) {
this.strength = strength;
},
// 确认修改密码
saveEditPass() {
let params = {
const params = {
password: this.md5(this.editPasswordForm.oldPassword),
newPassword: this.md5(this.editPasswordForm.newPassword),
passStrength: this.md5(this.strength),
};
this.$refs["editPasswordForm"].validate((valid) => {
if (valid) {
this.$refs.editPasswordForm.validate((valid) => {
if (!valid) return;
this.savePassLoading = true;
changePass(params).then((res) => {
this.savePassLoading = false;
if (res.success) {
this.$Modal.success({
title: "修改密码成功",
content: "修改密码成功,需重新登录",
onOk: () => {
ElMessageBox.alert("修改密码成功,需重新登录", "修改密码成功", {
confirmButtonText: "确定",
callback: () => {
this.$store.commit("logout", this);
this.$router.push({
name: "login",
});
this.$router.push({ name: "login" });
},
});
}
});
}
});
},
// 取消修改密码
cancelEditPass() {
this.$store.commit("removeTag", "change_password");
localStorage.pageOpenedList = JSON.stringify(
this.$store.state.app.pageOpenedList
);
let lastPageName = "";
let length = this.$store.state.app.pageOpenedList.length;
if (length > 1) {
lastPageName = this.$store.state.app.pageOpenedList[length - 1].name;
} else {
lastPageName = this.$store.state.app.pageOpenedList[0].name;
}
this.$router.push({
name: lastPageName,
});
localStorage.pageOpenedList = JSON.stringify(this.$store.state.app.pageOpenedList);
const list = this.$store.state.app.pageOpenedList;
const lastPageName = list.length > 1 ? list[list.length - 1].name : list[0].name;
this.$router.push({ name: lastPageName });
},
},
}
};
</script>

View File

@@ -1,51 +1,66 @@
<template>
<div class="search">
<Card>
<Row class="operation">
<Button @click="add" type="primary">添加</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
sortable="custom"
@on-selection-change="changeSelect"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
<Modal
:title="modalTitle"
v-model="modalVisible"
:mask-closable="false"
:width="500"
>
<Form ref="form" :model="form" :label-width="100" :rules="formValidate">
<FormItem label="自定义分词" prop="name">
<Input v-model="form.name" clearable style="width: 100%" />
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit"
>提交</Button
>
<el-card>
<div class="operation">
<el-button type="primary" @click="add">添加</el-button>
</div>
</Modal>
<el-table
ref="table"
v-loading="loading"
border
:data="data"
style="width: 100%"
row-key="id"
@selection-change="changeSelect"
>
<el-table-column type="selection" width="60" align="center" />
<el-table-column prop="name" label="自定义分词" min-width="120" />
<el-table-column prop="createTime" label="创建时间" width="200" />
<el-table-column prop="updateTime" label="更新时间" width="200" />
<el-table-column prop="createBy" label="操作人" min-width="150" />
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<template v-if="row">
<a class="link-text" @click="detail(row)">修改</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
<el-form-item label="自定义分词" prop="name">
<el-input v-model="form.name" clearable style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -54,118 +69,36 @@ import {
getCustomWordsPage,
delCustom,
insertCustomWords,
updateCustomWords
updateCustomWords,
} from "@/api/index";
import { regular } from "@/utils";
export default {
name: "custom-words",
data() {
return {
loading: true, // 表单加载状态
modalType: 0, // 添加或编辑标识
modalVisible: false, // 添加或编辑显示
modalTitle: "", // 添加或编辑标题
loading: true,
modalType: 0,
modalVisible: false,
modalTitle: "",
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
words: "",
},
form: {
// 添加或编辑表单对象初始化数据
name: "",
},
// 表单验证规则
formValidate: {
name: [
regular.REQUIRED,
regular.VARCHAR20
],
name: [regular.REQUIRED, regular.VARCHAR20],
},
submitLoading: false, // 添加或编辑提交状态
selectList: [], // 多选数据
selectCount: 0, // 多选计数
columns: [
// 表头
{
type: "selection",
width: 60,
align: "center",
},
{
title: "自定义分词",
key: "name",
minWidth: 120
},
{
title: "创建时间",
key: "createTime",
width: 200
},
{
title: "更新时间",
key: "updateTime",
width: 200
},
{
title: "操作人",
key: "createBy",
minWidth: 150
},
{
title: "操作",
key: "action",
align: "center",
fixed: "right",
width: 200,
render: (h, params) => {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.detail(params.row);
},
},
},
"修改"
),
h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.remove(params.row);
},
},
},
"删除"
),
]);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
submitLoading: false,
selectList: [],
selectCount: 0,
data: [],
total: 0,
};
},
methods: {
@@ -187,17 +120,14 @@ export default {
this.getDataList();
},
clearSelectAll() {
this.$refs.table.selectAll(false);
this.$refs.table?.clearSelection();
},
// 选中状态变更
changeSelect(e) {
this.selectList = e;
this.selectCount = e.length;
},
// 获取列表数据
getDataList() {
this.loading = true;
getCustomWordsPage(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
@@ -208,12 +138,10 @@ export default {
this.total = this.data.length;
this.loading = false;
},
// 提交数据
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
if (this.modalType == 0) {
delete this.form.id;
insertCustomWords(this.form).then((res) => {
@@ -226,7 +154,6 @@ export default {
});
} else {
this.form.id = this.id;
// 编辑
updateCustomWords(this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
@@ -239,16 +166,13 @@ export default {
}
});
},
// 添加
add() {
this.modalType = 0;
this.modalTitle = "添加";
this.form = {}
this.$refs.form.resetFields();
this.form = {};
this.$refs.form?.resetFields();
this.modalVisible = true;
},
// 修改
detail(v) {
this.modalType = 1;
this.id = v.id;
@@ -256,15 +180,12 @@ export default {
this.modalVisible = true;
this.form.name = v.name;
},
// 删除
remove(v) {
this.$Modal.confirm({
title: "确认删除",
// 记得确认修改此处
content: "您确认要删除 " + v.name + " ?",
loading: true,
onOk: () => {
// 删除
delCustom(v.id).then((res) => {
this.$Modal.remove();
if (res.success) {
@@ -275,7 +196,6 @@ export default {
},
});
},
// 批量删除
delAll() {
if (this.selectCount <= 0) {
this.$Message.warning("您还未选择要删除的数据");
@@ -291,8 +211,7 @@ export default {
ids += e.id + ",";
});
ids = ids.substring(0, ids.length - 1);
// 批量删除
delSensitive(ids).then((res) => {
delCustom(ids).then((res) => {
this.$Modal.remove();
if (res.success) {
this.$Message.success("操作成功");

View File

@@ -1,95 +1,144 @@
<template>
<div>
<Card>
<Row @keydown.enter.native.prevent="handleSearch">
<Form
<div class="search">
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
:label-width="70"
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<Form-item label="会员名称" prop="memberName">
<Input
type="text"
<el-form-item label="会员名称" prop="memberName">
<el-input
v-model="searchForm.memberName"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="状态">
<Select
v-model="searchForm.distributionStatus"
style="width: 240px"
>
<Option
</el-form-item>
<el-form-item label="状态">
<el-select v-model="searchForm.distributionStatus" style="width: 240px">
<el-option
v-for="item in distributionStatusList"
:value="item.value"
:key="item.value"
>{{ item.label }}
</Option
>
</Select>
</Form-item>
<Button
@click="handleSearch"
type="primary"
icon="ios-search"
class="search-btn"
>搜索
</Button
>
</Form>
</Row>
</Card>
<Card>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<el-table
ref="table"
v-loading="loading"
border
:data="data"
class="mt_10"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
style="width: 100%"
>
<el-table-column prop="memberName" label="会员名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="name" label="姓名" min-width="100" />
<el-table-column prop="idNumber" label="身份证号" min-width="120" />
<el-table-column prop="settlementBankAccountName" label="结算银行开户行名称" min-width="120" />
<el-table-column prop="settlementBankAccountNum" label="结算银行开户账号" min-width="120" />
<el-table-column prop="settlementBankBranchName" label="结算银行开户支行名称" min-width="120" />
<el-table-column prop="distributionOrderCount" label="推广单数" width="150" />
<el-table-column label="分销订单金额" width="150">
<template #default="{ row }">
<priceColorScheme v-if="row" :value="row.distributionOrderPrice" :color="$mainColor" />
</template>
</el-table-column>
<el-table-column label="分销金额" width="150">
<template #default="{ row }">
<priceColorScheme v-if="row" :value="row.rebateTotal" :color="$mainColor" />
</template>
</el-table-column>
<el-table-column label="待提现金额" width="150">
<template #default="{ row }">
<priceColorScheme v-if="row" :value="row.canRebate" color="green" />
</template>
</el-table-column>
<el-table-column label="冻结金额" width="150">
<template #default="{ row }">
<priceColorScheme v-if="row" :value="row.commissionFrozen" color="#347dda" />
</template>
</el-table-column>
<el-table-column label="状态" width="150">
<template #default="{ row }">
<template v-if="row">
<el-tag v-if="row.distributionStatus == 'PASS'" type="success">通过</el-tag>
<el-tag v-else-if="row.distributionStatus == 'APPLY'" type="primary">待审核</el-tag>
<el-tag v-else-if="row.distributionStatus == 'RETREAT'" type="warning">清退</el-tag>
<el-tag v-else-if="row.distributionStatus == 'REFUSE'" type="danger">拒绝</el-tag>
</template>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<template v-if="row">
<a v-if="row.distributionStatus != 'RETREAT'" class="link-text" @click="retreat(row)">清退</a>
<a v-if="row.distributionStatus == 'RETREAT'" class="link-text" @click="resume(row)">恢复</a>
<span class="op-split">|</span>
<a class="link-text" @click="edit(row)">编辑</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
<Modal title="修改分销员" v-model="modalVisible" :mask-closable="false" :width="600">
<Form ref="distributionForm" :model="distributionForm" :label-width="150" :rules="distributionFormValidate">
<FormItem label="姓名" prop="name">
<Input v-model="distributionForm.name" clearable style="width: 100%"/>
</FormItem>
<FormItem label="身份证号" prop="idNumber">
<Input v-model="distributionForm.idNumber" clearable style="width: 100%"/>
</FormItem>
<FormItem label="结算银行开户行名称" prop="settlementBankAccountName">
<Input v-model="distributionForm.settlementBankAccountName" clearable style="width: 100%"/>
</FormItem>
<FormItem label="结算银行开户账号" prop="settlementBankAccountNum">
<Input v-model="distributionForm.settlementBankAccountNum" clearable style="width: 100%"/>
</FormItem>
<FormItem label="结算银行开户支行名称" prop="settlementBankBranchName">
<Input v-model="distributionForm.settlementBankBranchName" clearable style="width: 100%"/>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit">提交</Button>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</Modal>
</el-card>
<el-dialog
v-model="modalVisible"
title="修改分销员"
width="600px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form
ref="distributionForm"
:model="distributionForm"
label-width="150px"
:rules="distributionFormValidate"
>
<el-form-item label="姓名" prop="name">
<el-input v-model="distributionForm.name" clearable style="width: 100%" />
</el-form-item>
<el-form-item label="身份证号" prop="idNumber">
<el-input v-model="distributionForm.idNumber" clearable style="width: 100%" />
</el-form-item>
<el-form-item label="结算银行开户行名称" prop="settlementBankAccountName">
<el-input v-model="distributionForm.settlementBankAccountName" clearable style="width: 100%" />
</el-form-item>
<el-form-item label="结算银行开户账号" prop="settlementBankAccountNum">
<el-input v-model="distributionForm.settlementBankAccountNum" clearable style="width: 100%" />
</el-form-item>
<el-form-item label="结算银行开户支行名称" prop="settlementBankBranchName">
<el-input v-model="distributionForm.settlementBankBranchName" clearable style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -100,256 +149,50 @@ import {regular} from "@/utils";
export default {
name: "distribution",
components: {},
data() {
return {
distributionStatusList, // 分销状态
loading: true, // 表单加载状态
modalVisible: false, // 添加或编辑显示
distributionStatusList,
loading: true,
modalVisible: false,
distributionForm: {
name: "",
idNumber: "",
settlementBankAccountName: "",
settlementBankAccountNum: "",
settlementBankBranchName: ""
settlementBankBranchName: "",
},
// 表单验证规则
distributionFormValidate: {
name: [
regular.REQUIRED,
regular.VARCHAR20
],
idNumber: [
regular.REQUIRED,
regular.VARCHAR20
],
settlementBankAccountName: [
regular.REQUIRED,
regular.VARCHAR20
],
settlementBankAccountNum: [
regular.REQUIRED,
regular.VARCHAR20
],
settlementBankBranchName: [
regular.REQUIRED,
regular.VARCHAR20
],
name: [regular.REQUIRED, regular.VARCHAR20],
idNumber: [regular.REQUIRED, regular.VARCHAR20],
settlementBankAccountName: [regular.REQUIRED, regular.VARCHAR20],
settlementBankAccountNum: [regular.REQUIRED, regular.VARCHAR20],
settlementBankBranchName: [regular.REQUIRED, regular.VARCHAR20],
},
submitLoading: false, // 编辑提交状态
submitLoading: false,
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
pageNumber: 1,
pageSize: 20,
},
columns: [
{
title: "会员名称",
key: "memberName",
minWidth: 120,
tooltip: true,
},
{
title: "姓名",
key: "name",
minWidth: 100,
},
{
title: "身份证号",
key: "idNumber",
minWidth: 120,
},
{
title: "结算银行开户行名称",
key: "settlementBankAccountName",
minWidth: 120,
},
{
title: "结算银行开户账号",
key: "settlementBankAccountNum",
minWidth: 120,
},
{
title: "结算银行开户支行名称",
key: "settlementBankBranchName",
minWidth: 120,
},
{
title: "推广单数",
key: "distributionOrderCount",
minWidth: 120,
width: 150,
},
{
title: "分销订单金额",
key: "distributionOrderPrice",
width: 150,
sortable: false,
render: (h, params) => {
return h("priceColorScheme", {props: {value: params.row.distributionOrderPrice, color: this.$mainColor}});
},
},
{
title: "分销金额",
key: "rebateTotal",
width: 150,
sortable: false,
render: (h, params) => {
return h("priceColorScheme", {props: {value: params.row.rebateTotal, color: this.$mainColor}});
},
},
{
title: "待提现金额",
key: "canRebate",
width: 150,
sortable: false,
render: (h, params) => {
return h("priceColorScheme", {props: {value: params.row.canRebate, color: 'green'}});
},
},
{
title: "冻结金额",
key: "commissionFrozen",
width: 150,
sortable: false,
render: (h, params) => {
return h("priceColorScheme", {props: {value: params.row.commissionFrozen, color: '#347dda'}});
},
},
{
title: "状态",
key: "distributionStatus",
width: 150,
sortable: false,
render: (h, params) => {
if (params.row.distributionStatus == "PASS") {
return h("Tag", {props: {color: "green",},}, "通过");
} else if (params.row.distributionStatus == "APPLY") {
return h("Tag", {props: {color: "geekblue",},}, "待审核");
} else if (params.row.distributionStatus == "RETREAT") {
return h("Tag", {props: {color: "volcano",},}, "清退");
} else if (params.row.distributionStatus == "REFUSE") {
return h("Tag", {props: {color: "red",},}, "拒绝");
}
},
},
{
title: "操作",
key: "action",
align: "center",
fixed: "right",
width: 140,
render: (h, params) => {
return h(
"div",
{
style: {
display: "flex",
justifyContent: "center",
},
},
[
h(
"Button",
{
props: {
type: "error",
size: "small",
},
style: {
marginRight: "5px",
display:
params.row.distributionStatus != "RETREAT"
? "block"
: "none",
},
on: {
click: () => {
this.retreat(params.row);
},
},
},
"清退"
),
h(
"Button",
{
props: {
type: "success",
size: "small",
},
style: {
marginRight: "5px",
display:
params.row.distributionStatus == "RETREAT"
? "block"
: "none",
},
on: {
click: () => {
this.resume(params.row);
},
},
},
"恢复"
),
h(
"Button",
{
props: {
type: "info",
size: "small",
},
style: {
marginRight: "5px",
},
on: {
click: () => {
this.edit(params.row);
},
},
},
"编辑"
), h(
"div",
{
style: {
display: "flex",
justifyContent: "center",
},
})
]
);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
data: [],
total: 0,
};
},
methods: {
// 初始化数据
init() {
this.getDataList();
},
// 分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
changePage() {
this.getDataList();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
changePageSize() {
this.searchForm.pageNumber = 1;
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
// 获取列表数据
getDataList() {
this.loading = true;
this.searchForm.status = "PASS";
@@ -361,15 +204,12 @@ export default {
}
});
},
// 清退分销商
retreat(v) {
this.$Modal.confirm({
title: "提示",
// 记得确认修改此处
content: "您确认要清退 " + v.memberName + " ?",
loading: true,
onOk: () => {
// 删除
retreatDistribution(v.id).then((res) => {
this.$Modal.remove();
if (res.success) {
@@ -380,15 +220,12 @@ export default {
},
});
},
// 恢复分销商
resume(v) {
this.$Modal.confirm({
title: "提示",
// 记得确认修改此处
content: "您确认要恢复 " + v.memberName + " ?",
loading: true,
onOk: () => {
// 删除
resumeDistribution(v.id).then((res) => {
this.$Modal.remove();
if (res.success) {
@@ -399,10 +236,8 @@ export default {
},
});
},
// 编辑
edit(v) {
this.$refs.distributionForm.resetFields();
// 转换null为""
this.$refs.distributionForm?.resetFields();
for (let attr in v) {
if (v[attr] === null) {
v[attr] = "";
@@ -413,11 +248,9 @@ export default {
this.distributionForm = data;
this.modalVisible = true;
},
// 提交表单
handleSubmit() {
this.$refs.distributionForm.validate((valid) => {
if (valid) {
// 编辑
editDistribution(this.distributionForm).then((res) => {
this.submitLoading = false;
if (res.success) {

View File

@@ -1,56 +1,68 @@
<template>
<div>
<Card>
<Row @keydown.enter.native.prevent="handleSearch">
<Form
<div class="search">
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
:label-width="70"
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<Form-item label="会员名称" prop="memberName">
<Input
type="text"
<el-form-item label="会员名称" prop="memberName">
<el-input
v-model="searchForm.memberName"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</Form-item>
<Button
@click="handleSearch"
type="primary"
class="search-btn"
>搜索</Button
>
</Form>
</Row>
</Card>
<Card>
<Table
class="mt_10"
:loading="loading"
border
:columns="columns"
:data="data"
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<el-table
ref="table"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
v-loading="loading"
border
:data="data"
class="mt_10"
style="width: 100%"
>
<el-table-column prop="memberName" label="会员名称" min-width="100" show-overflow-tooltip />
<el-table-column prop="name" label="姓名" min-width="100" />
<el-table-column prop="idNumber" label="身份证号" min-width="120" />
<el-table-column prop="settlementBankAccountName" label="结算银行开户行名称" min-width="120" />
<el-table-column prop="settlementBankAccountNum" label="结算银行开户账号" min-width="120" />
<el-table-column prop="settlementBankBranchName" label="结算银行开户支行名称" min-width="120" />
<el-table-column prop="createTime" label="提交时间" min-width="100" />
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<div v-if="row" class="ops">
<a class="link-text" @click="audit(row, 'PASS')">通过</a>
<span class="op-split">|</span>
<a class="link-text" @click="audit(row, 'REFUSE')">拒绝</a>
</div>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</div>
</template>
@@ -61,115 +73,35 @@ export default {
name: "distributionApply",
data() {
return {
loading: true, // 表单加载状态
loading: true,
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
startDate: "", // 起始时间
endDate: "", // 终止时间
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
startDate: "",
endDate: "",
},
columns: [
{
title: "会员名称",
key: "memberName",
minWidth: 100,
tooltip: true,
},
{
title: "姓名",
key: "name",
minWidth: 100,
},
{
title: "身份证号",
key: "idNumber",
minWidth: 120,
},
{
title: "结算银行开户行名称",
key: "settlementBankAccountName",
minWidth: 120,
},
{
title: "结算银行开户账号",
key: "settlementBankAccountNum",
minWidth: 120,
},
{
title: "结算银行开户支行名称",
key: "settlementBankBranchName",
minWidth: 120,
},
{
title: "提交时间",
key: "createTime",
minWidth: 100,
},
{
title: "操作",
key: "action",
align: "center",
fixed: "right",
width: 200,
render: (h, params) => {
return h("div", { class: "ops" }, [
h(
"a",
{
on: {
click: () => {
this.audit(params.row, "PASS");
},
},
},
"通过"
),
h("span", {}, "|"),
h(
"a",
{
on: {
click: () => {
this.audit(params.row, "REFUSE");
},
},
},
"拒绝"
),
]);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
data: [],
total: 0,
};
},
methods: {
// 初始化数据
init() {
this.getDataList();
},
// 分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
this.clearSelectAll();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
changePage() {
this.getDataList();
},
changePageSize() {
this.searchForm.pageNumber = 1;
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
// 获取列表数据
getDataList() {
this.loading = true;
this.searchForm.distributionStatus = "APPLY";
@@ -180,10 +112,7 @@ export default {
this.total = res.result.total;
}
});
this.total = this.data.length;
this.loading = false;
},
//审核
audit(v, status) {
let test = "拒绝";
if (status == "PASS") {
@@ -194,7 +123,6 @@ export default {
};
this.$Modal.confirm({
title: "确认" + test,
// 记得确认修改此处
content: "您确认要" + test + " " + v.memberName + " ?",
loading: true,
onOk: () => {

View File

@@ -1,250 +1,201 @@
<template>
<div>
<Card>
<Form ref="searchForm" @keydown.enter.native.prevent="handleSearch" :model="searchForm" class="search-form">
<Form-item label="会员名称" class="flex" prop="memberName">
<Input
type="text" placeholder="请输入会员名称" v-model="searchForm.memberName" clearable
style="width: 240px"></Input>
</Form-item>
<Form-item label="编号" class="flex">
<Input
type="text" placeholder="请输入编号" v-model="searchForm.sn" clearable
style="width: 240px"></Input>
</Form-item>
<Form-item label="状态"
style="width: 240px">
<Select v-model="searchForm.distributionCashStatus" clearable style="width: 150px">
<Option v-for="item in cashStatusList" :value="item.value" :key="item.value">{{ item.label }}</Option>
</Select>
</Form-item>
<Form-item>
<Button @click="handleSearch" type="primary">搜索</Button>
</Form-item>
</Form>
</Card>
<Card>
<Table :loading="loading" border :columns="columns" :data="data" ref="table" class="mt_10"></Table>
<Row type="flex" justify="end" class="page padding-row">
<Page :current="searchForm.pageNumber" :total="total" :page-size="searchForm.pageSize" @on-change="changePage" @on-page-size-change="changePageSize" :page-size-opts="[20,50,100]" size="small" show-total show-elevator show-sizer></Page>
</Row>
</Card>
<Modal :title="modalTitle" v-model="modalVisible" :mask-closable='false' :width="500">
<Form ref="form" :model="form" :label-width="100" >
<FormItem label="编号">
<Input disabled v-model="form.sn" clearable style="width:100%"/>
</FormItem>
<FormItem label="会员名称">
<Input disabled v-model="form.distributionName" clearable style="width:100%"/>
</FormItem>
<FormItem label="金额">
<Input disabled v-model="form.price" clearable style="width:100%"/>
</FormItem>
<FormItem label="是否通过" prop="result" v-if="handleStatus =='edit'">
<RadioGroup v-model="result" type="button" button-style="solid">
<Radio label="VIA_AUDITING">通过</Radio>
<Radio label="FAIL_AUDITING">拒绝</Radio>
</RadioGroup>
</FormItem>
</Form>
<div slot="footer" v-if="handleStatus == 'edit'">
<Button type="text" @click="modalVisible=false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit">提交</Button>
<div class="search">
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
class="search-form"
@keyup.enter="handleSearch"
>
<el-form-item label="会员名称" class="flex" prop="memberName">
<el-input
v-model="searchForm.memberName"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item label="编号" class="flex">
<el-input
v-model="searchForm.sn"
placeholder="请输入编号"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item label="状态" style="width: 240px">
<el-select v-model="searchForm.distributionCashStatus" clearable style="width: 150px">
<el-option
v-for="item in cashStatusList"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<el-table
ref="table"
v-loading="loading"
border
:data="data"
class="mt_10"
style="width: 100%"
>
<el-table-column prop="sn" label="编号" min-width="200" />
<el-table-column prop="distributionName" label="会员名称" min-width="120" />
<el-table-column label="申请金额" min-width="90">
<template #default="{ row }">
<priceColorScheme v-if="row" :value="row.price" :color="$mainColor" />
</template>
</el-table-column>
<el-table-column prop="createTime" label="申请时间" min-width="130" />
<el-table-column prop="name" label="姓名" min-width="100" />
<el-table-column prop="idNumber" label="身份证号" min-width="120" />
<el-table-column prop="settlementBankAccountName" label="结算银行开户行名称" min-width="120" />
<el-table-column prop="settlementBankAccountNum" label="结算银行开户账号" min-width="120" />
<el-table-column prop="settlementBankBranchName" label="结算银行开户支行名称" min-width="120" />
<el-table-column prop="updateTime" label="处理时间" min-width="130" />
<el-table-column label="状态" min-width="100">
<template #default="{ row }">
<span v-if="row">
<span v-if="row.distributionCashStatus == 'APPLY'">待处理</span>
<span v-else-if="row.distributionCashStatus == 'VIA_AUDITING'">通过</span>
<span v-else-if="row.distributionCashStatus == 'FAIL_AUDITING'">审核拒绝</span>
</span>
</template>
</el-table-column>
<el-table-column label="操作" width="130" align="center" fixed="right">
<template #default="{ row }">
<div v-if="row" class="ops">
<a class="link-text" v-if="row.distributionCashStatus != 'APPLY'" @click="view(row)">查看</a>
<a class="link-text" v-else @click="edit(row)">审核</a>
</div>
</Modal>
</template>
</el-table-column>
</el-table>
<div class="mt_10 page padding-row" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="form" :model="form" label-width="100px">
<el-form-item label="编号">
<el-input v-model="form.sn" disabled clearable style="width: 100%" />
</el-form-item>
<el-form-item label="会员名称">
<el-input v-model="form.distributionName" disabled clearable style="width: 100%" />
</el-form-item>
<el-form-item label="金额">
<el-input v-model="form.price" disabled clearable style="width: 100%" />
</el-form-item>
<el-form-item v-if="handleStatus == 'edit'" label="是否通过" prop="result">
<el-radio-group v-model="result">
<el-radio-button value="VIA_AUDITING">通过</el-radio-button>
<el-radio-button value="FAIL_AUDITING">拒绝</el-radio-button>
</el-radio-group>
</el-form-item>
</el-form>
<template v-if="handleStatus == 'edit'" #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</div>
</template>
<script>
import {
auditDistributionCash,
getDistributionCash
} from "@/api/distribution";
import { cashStatusList } from './dataJson';
import { auditDistributionCash, getDistributionCash } from "@/api/distribution";
import { cashStatusList } from "./dataJson";
export default {
name: "distributionCash",
data() {
return {
cashStatusList, // 状态列表
loading: true, // 表单加载状态
modalVisible: false, // 添加或编辑显示
modalTitle: "", // 添加或编辑标题
result: 'FAIL_AUDITING', // 是否通过
searchForm: { // 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
cashStatusList,
loading: true,
modalVisible: false,
modalTitle: "",
result: "FAIL_AUDITING",
searchForm: {
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
},
handleStatus:'edit',// 判断是编辑还是查看
form: { // 添加或编辑表单对象初始化数据
handleStatus: "edit",
form: {
sn: "",
memberName: "",
price: "",
},
submitLoading: false, // 添加或编辑提交状态
columns: [
{
title: "编号",
key: "sn",
minWidth: 200
},
{
title: "会员名称",
key: "distributionName",
minWidth: 120
},
{
title: "申请金额",
key: "price",
minWidth: 90,
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.price,color:this.$mainColor}} );
},
},
{
title: "申请时间",
key: "createTime",
minWidth: 130
},
{
title: "姓名",
key: "name",
minWidth: 100,
},
{
title: "身份证号",
key: "idNumber",
minWidth: 120,
},
{
title: "结算银行开户行名称",
key: "settlementBankAccountName",
minWidth: 120,
},
{
title: "结算银行开户账号",
key: "settlementBankAccountNum",
minWidth: 120,
},
{
title: "结算银行开户支行名称",
key: "settlementBankBranchName",
minWidth: 120,
},
{
title: "处理时间",
key: "updateTime",
minWidth: 130
},
{
title: "状态",
key: "distributionCashStatus",
minWidth: 100,
render: (h, params) => {
if (params.row.distributionCashStatus == 'APPLY') {
return h("div", "待处理");
}
if (params.row.distributionCashStatus == 'VIA_AUDITING') {
return h("div", "通过");
}
if (params.row.distributionCashStatus == 'FAIL_AUDITING') {
return h("div", "审核拒绝");
}
},
},
{
title: "操作",
key: "action",
align: "center",
fixed: "right",
width: 130,
render: (h, params) => {
if (params.row.distributionCashStatus != 'APPLY') {
return h("div", { class: "ops" }, [
h(
"a",
{
on: {
click: () => {
this.view(params.row);
}
}
},
"查看"
),
]);
} else {
return h("div", { class: "ops" }, [
h(
"a",
{
on: {
click: () => {
this.edit(params.row);
}
}
},
"审核"
),
]);
}
}
}
],
data: [], // 表单数据
total: 0 // 表单数据总数
submitLoading: false,
data: [],
total: 0,
};
},
methods: {
// 初始化数据
init() {
this.getDataList();
},
// 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
changePage() {
this.getDataList();
},
// 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
changePageSize() {
this.searchForm.pageNumber = 1;
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
// 获取列表数据
getDataList() {
this.loading = true;
// 带多条件搜索参数获取表单数据 请自行修改接口
getDistributionCash(this.searchForm).then(res => {
getDistributionCash(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
});
this.total = this.data.length;
this.loading = false;
},
// 通过还是拒绝申请
handleSubmit() {
let result = "拒绝"
if(this.result == 'VIA_AUDITING'){
result = "通过"
let result = "拒绝";
if (this.result == "VIA_AUDITING") {
result = "通过";
}
this.$refs.form.validate(valid => {
this.$refs.form.validate((valid) => {
if (valid) {
this.$Modal.confirm({
title: "确认审核",
content: "您确认要审核" + result + "么?",
loading: true,
onOk: () => {
auditDistributionCash(this.form.id,{result:this.result}).then(res => {
auditDistributionCash(this.form.id, { result: this.result }).then((res) => {
if (res.success) {
this.$Modal.remove();
this.$Message.success("审核成功");
@@ -254,17 +205,15 @@ export default {
this.modalVisible = false;
}
});
}
})
},
});
}
});
},
// 弹出modal 审核
edit(v) {
this.modalTitle = "审核";
this.handleStatus = 'edit';
this.$refs.form.resetFields();
// 转换null为""
this.handleStatus = "edit";
this.$refs.form?.resetFields();
for (let attr in v) {
if (v[attr] === null) {
v[attr] = "";
@@ -273,12 +222,10 @@ export default {
this.form = JSON.parse(JSON.stringify(v));
this.modalVisible = true;
},
// 弹出modal 查看
view(v) {
this.modalTitle = "查看";
this.handleStatus = 'view';
this.$refs.form.resetFields();
// 转换null为""
this.handleStatus = "view";
this.$refs.form?.resetFields();
for (let attr in v) {
if (v[attr] === null) {
v[attr] = "";
@@ -288,12 +235,11 @@ export default {
let data = JSON.parse(str);
this.form = data;
this.modalVisible = true;
}
},
},
mounted() {
this.init();
}
},
};
</script>
<style lang="scss" scoped>

View File

@@ -1,214 +1,182 @@
<template>
<div>
<Card>
<Form @keydown.enter.native.prevent="handleSearch" ref="searchForm" :model="searchForm" inline :label-width="70"
class="search-form">
<Form-item label="商品名称" prop="goodsName">
<Input type="text" v-model="searchForm.goodsName" placeholder="请输入商品名称" clearable style="width: 240px" />
</Form-item>
<Button @click="handleSearch" type="primary" icon="ios-search" class="search-btn">搜索</Button>
</Form>
</Card>
<Card>
<Row class="operation" style="margin:10px 0;">
<Button @click="delAll" type="primary">批量下架</Button>
</Row>
<Table :loading="loading" border :columns="columns" :data="data" ref="table" sortable="custom"
@on-selection-change="changeSelect">
<template slot="goodsName" slot-scope="{row}">
<div>
<div class="div-zoom">
<a @click="linkTo(row.goodsId,row.skuId)">{{row.goodsName}}</a>
</div>
<Poptip trigger="hover" title="扫码在手机中查看" transfer>
<div slot="content">
<vue-qr :text="wapLinkTo(row.goodsId,row.skuId)" :margin="0" colorDark="#000" colorLight="#fff"
:size="150"></vue-qr>
</div>
<img src="../../assets/qrcode.svg" class="hover-pointer" width="20" height="20" alt="">
</Poptip>
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
label-width="70px"
class="search-form"
@keyup.enter.prevent="handleSearch"
>
<el-form-item label="商品名称" prop="goodsName">
<el-input
v-model="searchForm.goodsName"
placeholder="请输入商品名称"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<div class="operation" style="margin: 10px 0">
<el-button type="primary" @click="delAll">批量下架</el-button>
</div>
<el-table
ref="table"
v-loading="loading"
border
:data="data"
class="mt_10"
style="width: 100%"
@selection-change="changeSelect"
>
<el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="商品图片" width="120" align="center" fixed="left">
<template #default="{ row }">
<img
v-if="row"
:src="row.thumbnail || ''"
alt="商品图"
style="cursor: pointer; width: 80px; height: 60px; margin: 10px 0; object-fit: contain"
/>
</template>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page :current="searchForm.pageNumber" :total="total" :page-size="searchForm.pageSize" @on-change="changePage"
@on-page-size-change="changePageSize" :page-size-opts="[20,50,100]" size="small" show-total show-elevator
show-sizer></Page>
</Row>
</Card>
</el-table-column>
<el-table-column label="商品名称" min-width="220" show-overflow-tooltip>
<template #default="{ row }">
<template v-if="row">
<div class="div-zoom">
<a class="link-text" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
</div>
<el-popover trigger="hover" title="扫码在手机中查看" placement="top" width="180">
<template #reference>
<img
src="../../assets/qrcode.svg"
class="hover-pointer"
width="20"
height="20"
alt="qrcode"
/>
</template>
<vue-qr
:text="wapLinkTo(row.goodsId, row.skuId)"
:margin="0"
color-dark="#000"
color-light="#fff"
:size="150"
/>
</el-popover>
</template>
</template>
</el-table-column>
<el-table-column label="商品价格" min-width="110">
<template #default="{ row }">
<span v-if="row" :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, "") }}</span>
</template>
</el-table-column>
<el-table-column prop="quantity" label="库存" min-width="80" />
<el-table-column prop="createTime" label="添加时间" min-width="160" />
<el-table-column prop="storeName" label="店铺名称" min-width="120" show-overflow-tooltip />
<el-table-column label="佣金金额" min-width="110">
<template #default="{ row }">
<span v-if="row" :style="{ color: $mainColor }">{{ $filters.unitPrice(row.commission, "") }}</span>
</template>
</el-table-column>
<el-table-column label="操作" min-width="100" align="center" fixed="right">
<template #default="{ row }">
<a v-if="row" class="link-text" @click="remove(row)">下架</a>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</div>
</template>
<script>
import { delDistributionGoods, getDistributionGoods } from "@/api/distribution";
import vueQr from "vue-qr";
export default {
components: {
"vue-qr": vueQr,
},
name: "distributionGoods",
components: { vueQr },
data() {
return {
loading: true, // 表单加载状态
loading: true,
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
},
selectList: [], // 多选数据
selectCount: 0, // 多选计数
columns: [
// 表头
{
type: "selection",
width: 60,
align: "center",
fixed: "left",
},
{
title: "商品图片",
fixed: "left",
key: "thumbnail",
width: 120,
align: "center",
render: (h, params) => {
return h("img", {
attrs: {
src: params.row.thumbnail || '',
alt: "加载图片失败",
},
style: {
cursor: "pointer",
width: "80px",
height: "60px",
margin: "10px 0",
"object-fit": "contain",
},
});
},
},
{
title: "商品名称",
slot: "goodsName",
minWidth: 200,
tooltip: true,
},
{
title: "商品价格",
key: "price",
minWidth: 100,
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.price,color:this.$mainColor}} );
},
},
{
title: "库存",
key: "quantity",
minWidth: 80,
},
{
title: "添加时间",
key: "createTime",
minWidth: 100,
},
{
title: "店铺名称",
key: "storeName",
minWidth: 100,
tooltip: true,
},
{
title: "佣金金额",
key: "commission",
minWidth: 100,
sortable: false,
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.commission,color:this.$mainColor}} );
},
},
{
title: "操作",
key: "action",
align: "center",
fixed: "right",
minWidth: 100,
render: (h, params) => {
return h("div", { class: "ops" }, [
h(
"a",
{
on: {
click: () => {
this.remove(params.row);
},
},
},
"下架"
),
]);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
selectList: [],
selectCount: 0,
data: [],
total: 0,
};
},
methods: {
// 初始化数据
init() {
this.getDataList();
},
// 分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
this.clearSelectAll();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
// 清除选中状态
clearSelectAll() {
this.$refs.table.selectAll(false);
this.$refs.table?.clearSelection();
},
// 选中后赋值
changeSelect(e) {
this.selectList = e;
this.selectCount = e.length;
},
// 获取列表数据
getDataList() {
this.loading = true;
getDistributionGoods(this.searchForm).then((res) => {
this.loading = false;
getDistributionGoods(this.searchForm)
.then((res) => {
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
});
this.total = this.data.length;
})
.finally(() => {
this.loading = false;
});
},
// 下架商品
remove(v) {
this.$Modal.confirm({
title: "确认下架",
content: "您确认要下架么?",
loading: true,
onOk: () => {
// 下架
delDistributionGoods(v.id).then((res) => {
this.$Modal.remove();
if (res.success) {
@@ -219,7 +187,6 @@ export default {
},
});
},
// 批量下架
delAll() {
if (this.selectCount <= 0) {
this.$Message.warning("您还未选择要下架的数据");
@@ -230,11 +197,7 @@ export default {
content: "您确认要下架所选的 " + this.selectCount + " 条数据?",
loading: true,
onOk: () => {
let ids = [];
this.selectList.forEach((item) => {
ids.push(item.id);
});
// 批量下架
const ids = this.selectList.map((item) => item.id);
delDistributionGoods(ids.toString()).then((res) => {
this.$Modal.remove();
if (res.success) {
@@ -252,17 +215,24 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.ops a {
color: #2d8cf0;
.link-text {
color: #409eff;
cursor: pointer;
text-decoration: none;
}
.ops span {
display: inline-block;
margin: 0 8px;
color: #dcdee2;
.div-zoom {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 4px;
}
.hover-pointer {
cursor: pointer;
vertical-align: middle;
}
.mt_10 {
margin-top: 10px;
}
</style>

View File

@@ -1,251 +1,261 @@
<template>
<div>
<Card>
<Form ref="searchForm" @keydown.enter.native="handleSearch" :model="searchForm" inline :label-width="70" class="search-form">
<Form-item label="订单编号" prop="orderSn">
<Input
type="text"
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<el-form-item label="订单编号" prop="orderSn">
<el-input
v-model="searchForm.orderSn"
placeholder="请输入订单编号"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="分销商" prop="distributionName">
<Input
type="text"
</el-form-item>
<el-form-item label="分销商" prop="distributionName">
<el-input
v-model="searchForm.distributionName"
placeholder="请输入分销商名称"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="店铺名称">
<Select v-model="searchForm.storeId" placeholder="请选择" @on-query-change="searchChange" filterable
clearable style="width: 240px">
<Option v-for="item in shopList" :value="item.id" :key="item.id">{{ item.storeName }}</Option>
</Select>
</Form-item>
<Form-item label="订单时间">
<DatePicker type="daterange" v-model="timeRange" format="yyyy-MM-dd" placeholder="选择时间"
style="width: 240px"></DatePicker>
</Form-item>
<Button @click="handleSearch" type="primary" icon="ios-search" class="search-btn">搜索</Button>
</Form>
</Card>
<Card>
<Table :loading="loading" border :columns="columns" :data="data" ref="table" class="mt_10">
<template slot-scope="{row}" slot="goodsMsg">
<div class="goods-msg">
<img :src="row.image" width="60" height="60" alt="">
</el-form-item>
<el-form-item label="店铺名称">
<el-select
v-model="searchForm.storeId"
placeholder="请选择"
filterable
remote
:remote-method="searchChange"
clearable
style="width: 240px"
>
<el-option
v-for="item in shopList"
:key="item.id"
:label="item.storeName"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="订单时间">
<el-date-picker
v-model="timeRange"
type="daterange"
value-format="YYYY-MM-DD"
start-placeholder="开始日期"
end-placeholder="结束日期"
placeholder="选择时间"
style="width: 240px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<el-table
ref="table"
v-loading="loading"
border
:data="data"
class="mt_10"
style="width: 100%"
>
<el-table-column
prop="orderSn"
label="订单编号"
min-width="180"
fixed="left"
show-overflow-tooltip
/>
<el-table-column label="商品信息" min-width="200">
<template #default="{ row }">
<div v-if="row" class="goods-msg">
<img :src="row.image" width="60" height="60" alt="" />
<div>
<div class="div-zoom">
<a @click="linkTo(row.goodsId,row.skuId)">{{row.goodsName}}</a>
<a class="link-text" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
</div>
<div style="color: #999; font-size: 10px">数量x{{ row.num }}</div>
<Poptip trigger="hover" title="扫码在手机中查看" transfer>
<div slot="content">
<vue-qr :text="wapLinkTo(row.goodsId,row.skuId)" :margin="0" colorDark="#000" colorLight="#fff" :size="150"></vue-qr>
</div>
<img src="../../assets/qrcode.svg" class="hover-pointer" width="20" height="20" alt="">
</Poptip>
<el-popover trigger="hover" title="扫码在手机中查看" placement="top" width="180">
<template #reference>
<img
src="../../assets/qrcode.svg"
class="hover-pointer"
width="20"
height="20"
alt="qrcode"
/>
</template>
<vue-qr
:text="wapLinkTo(row.goodsId, row.skuId)"
:margin="0"
color-dark="#000"
color-light="#fff"
:size="150"
/>
</el-popover>
</div>
</div>
</template>
<template slot-scope="{row}" slot="distributionOrderStatus">
<Tag :color="filterStatusColor(row.distributionOrderStatus)">{{filterStatus(row.distributionOrderStatus)}}</Tag>
</el-table-column>
<el-table-column prop="distributionName" label="分销商" min-width="100" show-overflow-tooltip />
<el-table-column prop="storeName" label="店铺名称" min-width="100" show-overflow-tooltip />
<el-table-column label="状态" min-width="90">
<template #default="{ row }">
<el-tag v-if="row" :type="filterStatusTagType(row.distributionOrderStatus)">
{{ filterStatus(row.distributionOrderStatus) }}
</el-tag>
</template>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page :current="searchForm.pageNumber" :total="total" :page-size="searchForm.pageSize"
@on-change="changePage" @on-page-size-change="changePageSize" :page-size-opts="[20,50,100]"
size="small" show-total show-elevator show-sizer></Page>
</Row>
</Card>
</el-table-column>
<el-table-column label="佣金金额" min-width="100">
<template #default="{ row }">
<span v-if="row" :style="{ color: $mainColor }">
{{ $filters.unitPrice(row.rebate, "") }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" min-width="160" fixed="right" />
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</div>
</template>
<script>
import {
getDistributionOrder
} from "@/api/distribution";
import {orderStatusList} from './dataJson'
import {getShopListData} from '@/api/shops'
import vueQr from 'vue-qr'
import { getDistributionOrder } from "@/api/distribution";
import { orderStatusList } from "./dataJson";
import { getShopListData } from "@/api/shops";
import vueQr from "vue-qr";
export default {
name: "distributionOrder",
components: {
"vue-qr":vueQr
},
components: { vueQr },
data() {
return {
timeRange: [], // 范围时间
orderStatusList, // 订单状态列表
shopList: [], // 店铺列表
distributionId: this.$route.query.id, // 分销id
loading: true, // 表单加载状态
searchForm: { // 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
timeRange: [],
orderStatusList,
shopList: [],
distributionId: this.$route.query.id,
loading: true,
searchForm: {
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order:"desc"
order: "desc",
},
columns: [
{
title: "订单编号",
key: "orderSn",
minWidth: 180,
fixed: "left",
tooltip: true
},
{
title: '商品信息',
slot: 'goodsMsg',
minWidth: 150
},
{
title: "分销商",
key: "distributionName",
tooltip: true,
minWidth:80,
},
{
title: "店铺名称",
key: "storeName",
minWidth:80,
tooltip: true
},
{
title: "状态",
slot: "distributionOrderStatus",
minWidth:80,
},
{
title: "佣金金额",
key: "rebate",
minWidth:80,
sortable: false,
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.rebate,color:this.$mainColor}} );
},
},
{
fixed: "right",
title: "创建时间",
key: "createTime",
minWidth:100,
sortable: false,
}
],
data: [], // 表单数据
total: 0 // 表单数据总数
data: [],
total: 0,
};
},
watch: {
$route(e) {
this.distributionId = e.query.id ? e.query.id : undefined;
this.getDataList();
},
},
methods: {
// 初始化数据
init() {
this.getDataList();
this.getShopList()
this.getShopList();
},
//分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
changePage() {
this.getDataList();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
changePageSize() {
this.searchForm.pageNumber = 1;
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
// 获取列表数据
getDataList() {
this.searchForm.distributionId = this.distributionId;
this.loading = true;
if (this.timeRange && this.timeRange[0]) {
let startTime = this.timeRange[0]
let endTime = this.timeRange[1]
this.searchForm.startTime = this.$options.filters.unixToDate(startTime / 1000)
this.searchForm.endTime = this.$options.filters.unixToDate(endTime / 1000)
if (this.timeRange && this.timeRange[0] && this.timeRange[1]) {
const startTime = new Date(this.timeRange[0]).getTime();
const endTime = new Date(this.timeRange[1]).getTime();
this.searchForm.startTime = this.$filters.unixToDate(startTime / 1000);
this.searchForm.endTime = this.$filters.unixToDate(endTime / 1000);
} else {
this.searchForm.startTime = null;
this.searchForm.endTime = null;
}
// 带多条件搜索参数获取表单数据 请自行修改接口
getDistributionOrder(this.searchForm).then(res => {
getDistributionOrder(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
});
this.total = this.data.length;
this.loading = false;
},
getShopList(val) { // 获取店铺列表 搜索用
getShopList(val) {
const params = {
pageNumber: 1,
pageSize: 20,
storeName: ''
}
if (val) {
params.storeName = val;
} else {
params.storeName = ''
}
getShopListData(params).then(res => {
this.shopList = res.result.records
})
storeName: val || "",
};
getShopListData(params).then((res) => {
this.shopList = res.result.records;
});
},
searchChange(val) { // 店铺搜索,键盘点击回调
this.getShopList(val)
searchChange(val) {
this.getShopList(val);
},
filterStatus (status) { // 过滤订单状态
filterStatus(status) {
const arr = [
{status: 'NO_COMPLETED', title: '未完成'},
{status: 'COMPLETE', title: '完成'},
{status: 'REFUND', title: '退款'},
]
{ status: "NO_COMPLETED", title: "未完成" },
{ status: "COMPLETE", title: "完成" },
{ status: "REFUND", title: "退款" },
];
for (let i = 0; i < arr.length; i++) {
if (arr[i].status === status) {
return arr[i].title;
}
}
return '未完成'; // 默认返回未完成
return "未完成";
},
filterStatusColor (status) { // 状态tag标签颜色
filterStatusTagType(status) {
const arr = [
{status: 'NO_COMPLETED', color: 'orange'},
{status: 'COMPLETE', color: 'green'},
{status: 'REFUND', color: 'red'},
]
{ status: "NO_COMPLETED", type: "warning" },
{ status: "COMPLETE", type: "success" },
{ status: "REFUND", type: "danger" },
];
for (let i = 0; i < arr.length; i++) {
if (arr[i].status === status) {
return arr[i].color;
return arr[i].type;
}
}
return 'orange'; // 默认返回橙色
}
return "warning";
},
},
mounted() {
this.init();
},
watch: {
$route(e) { // 监听路由参数变化调取接口
this.distributionId = e.query.id ? e.query.id : undefined;
this.getDataList();
}
}
};
</script>
<style lang="scss">
.goods-msg {
display: flex;
@@ -254,5 +264,18 @@
margin-left: 10px;
}
}
</style>
.link-text {
color: #409eff;
cursor: pointer;
text-decoration: none;
}
.hover-pointer {
cursor: pointer;
}
.mt_10 {
margin-top: 10px;
}
</style>

View File

@@ -1,22 +1,17 @@
<template>
<div style="background-color: #fff;">
<Form ref="form" :model="form" :rules="formRule" :label-width="120" style="padding: 10px;">
<Divider orientation="left">分销设置</Divider>
<FormItem label="是否开启分销" prop="isOpen">
<i-switch size="large" v-model="form.isOpen" :true-value="true" :false-value="false">
<span slot="open">开启</span>
<span slot="close">关闭</span>
</i-switch>
</FormItem>
<FormItem label="分销关系绑定天数" prop="distributionDay">
<InputNumber :min="1" :max="365" style="width:100px;" v-model="form.distributionDay"></InputNumber>
</FormItem>
<FormItem>
<Button type="primary" @click="submit">保存</Button>
</FormItem>
</Form>
<div style="background-color: #fff">
<el-form ref="form" :model="form" :rules="formRule" label-width="140px" style="padding: 10px">
<el-divider content-position="left">分销设置</el-divider>
<el-form-item label="是否开启分销" prop="isOpen">
<el-switch v-model="form.isOpen" active-text="开启" inactive-text="关闭" />
</el-form-item>
<el-form-item label="分销关系绑定天数" prop="distributionDay">
<el-input-number v-model="form.distributionDay" :min="1" :max="365" style="width: 120px" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit">保存</el-button>
</el-form-item>
</el-form>
</div>
</template>
@@ -28,26 +23,22 @@ export default {
data() {
return {
form: {
// 添加或编辑表单对象初始化数据
isOpen: true,
distributionDay: 0, //分销关系绑定天数
distributionDay: 1,
},
formRule: {
isOpen: [
regular.REQUIRED
],
distributionDay: [
regular.REQUIRED
],
}
isOpen: [regular.REQUIRED],
distributionDay: [regular.REQUIRED],
},
};
},
mounted() {
this.init();
},
methods: {
// 初始化数据
init() {
this.getDataList();
},
// 获取分销设置数据
getDataList() {
getSetting("DISTRIBUTION_SETTING").then((res) => {
if (res.success) {
@@ -55,20 +46,17 @@ export default {
}
});
},
// 提交api
submit() {
this.$refs.form.validate((valid) => {
if (!valid) return;
setSetting("DISTRIBUTION_SETTING", this.form).then((res) => {
if (res.success) {
this.$Message.success("操作成功");
this.getDataList();
}
});
});
},
},
mounted() {
this.init();
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@@ -1,19 +1,18 @@
<template>
<div class="error403">
<div class="error403-body-con">
<Card>
<div class="error403-body-con-title">4<span class="error403-0-span">
<Icon type="android-lock"></Icon>
</span><span class="error403-key-span">
<Icon size="220" type="ios-bolt"></Icon>
</span></div>
<el-card>
<div class="error403-body-con-title">
4<span class="error403-0-span">🔒</span><span class="error403-key-span"></span>
</div>
<p class="error403-body-con-message">You don't have permission</p>
<div class="error403-btn-con">
<Button @click="goHome" size="large" style="width: 200px;" type="text">返回首页</Button>
<Button @click="backPage" size="large" style="width: 200px;margin-left: 40px;" type="primary">返回上一页</Button>
<el-button size="large" style="width: 200px" @click="goHome">返回首页</el-button>
<el-button size="large" type="primary" style="width: 200px; margin-left: 40px" @click="backPage">
返回上一页
</el-button>
</div>
</Card>
</el-card>
</div>
</div>
</template>
@@ -22,11 +21,9 @@
export default {
name: "Error403",
methods: {
// 返回上一页
backPage() {
this.$router.go(-1);
},
// 返回首页
goHome() {
this.$router.push({
name: "home_index",
@@ -35,6 +32,7 @@ export default {
},
};
</script>
<style lang="scss" scoped>
@keyframes error403animation {
0% {
@@ -84,14 +82,8 @@ export default {
border: 20px solid #ed3f14;
color: #ed3f14;
margin-right: 10px;
i {
display: inline-block;
font-size: 120px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
font-size: 80px;
line-height: 130px;
}
.error403-key-span {
display: inline-block;
@@ -100,17 +92,10 @@ export default {
height: 190px;
border-radius: 50%;
margin-right: 10px;
i {
display: inline-block;
font-size: 190px;
position: absolute;
left: 20px;
transform: translate(-50%, -60%);
transform-origin: center bottom;
font-size: 80px;
animation: error403animation 2.8s ease 0s infinite;
}
}
}
&-message {
display: block;
text-align: center;
@@ -127,4 +112,3 @@ export default {
}
}
</style>

View File

@@ -1,27 +1,18 @@
<template>
<div class="error404">
<div class="error404-body-con">
<Card>
<el-card>
<div class="error404-body-con-title">
4<span><Icon type="ios-navigate-outline"></Icon></span>4
4<span>🧭</span>4
</div>
<p class="error404-body-con-message">
YOU&nbsp;&nbsp;LOOK&nbsp;&nbsp;LOST
</p>
<p class="error404-body-con-message">YOU&nbsp;&nbsp;LOOK&nbsp;&nbsp;LOST</p>
<div class="error404-btn-con">
<Button @click="goHome" size="large" style="width: 200px" type="text"
>返回首页</Button
>
<Button
@click="backPage"
size="large"
style="width: 200px; margin-left: 40px"
type="primary"
>返回上一页</Button
>
<el-button size="large" style="width: 200px" @click="goHome">返回首页</el-button>
<el-button size="large" type="primary" style="width: 200px; margin-left: 40px" @click="backPage">
返回上一页
</el-button>
</div>
</Card>
</el-card>
</div>
</div>
</template>
@@ -30,11 +21,9 @@
export default {
name: "Error404",
methods: {
// 返回上一页
backPage() {
this.$router.go(-1);
},
// 返回首页
goHome() {
this.$router.push({
name: "home_index",
@@ -43,6 +32,7 @@ export default {
},
};
</script>
<style lang="scss" scoped>
@keyframes error404animation {
0% {
@@ -83,7 +73,7 @@ export default {
span {
display: inline-block;
color: #19be6b;
font-size: 230px;
font-size: 120px;
animation: error404animation 3s ease 0s infinite alternate;
}
}

View File

@@ -1,30 +1,18 @@
<template>
<div class="error500">
<div class="error500-body-con">
<Card>
<el-card>
<div class="error500-body-con-title">
5<span class="error500-0-span"
><Icon type="social-freebsd-devil"></Icon></span
><span class="error500-0-span"
><Icon type="social-freebsd-devil"></Icon
></span>
5<span class="error500-0-span">😈</span><span class="error500-0-span">😈</span>
</div>
<p class="error500-body-con-message">Oops! the server is wrong</p>
<div class="error500-btn-con">
<Button @click="goHome" size="large" style="width: 200px" type="text"
>返回首页</Button
>
<Button
@click="backPage"
size="large"
style="width: 200px; margin-left: 40px"
type="primary"
>返回上一页</Button
>
<el-button size="large" style="width: 200px" @click="goHome">返回首页</el-button>
<el-button size="large" type="primary" style="width: 200px; margin-left: 40px" @click="backPage">
返回上一页
</el-button>
</div>
</Card>
</el-card>
</div>
</div>
</template>
@@ -33,11 +21,9 @@
export default {
name: "Error500",
methods: {
// 返回上一页
backPage() {
this.$router.go(-1);
},
// 返回首页
goHome() {
this.$router.push({
name: "home_index",
@@ -46,6 +32,7 @@ export default {
},
};
</script>
<style lang="scss" scoped>
@keyframes error500animation {
0% {
@@ -86,23 +73,17 @@ export default {
.error500-0-span {
display: inline-block;
position: relative;
width: 170px;
height: 170px;
width: 120px;
height: 120px;
border-radius: 50%;
border: 20px solid #ed3f14;
color: #ed3f14;
margin-right: 10px;
i {
display: inline-block;
font-size: 120px;
position: absolute;
bottom: -10px;
left: 10px;
transform-origin: center bottom;
font-size: 60px;
line-height: 80px;
animation: error500animation 3s ease 0s infinite alternate;
}
}
}
&-message {
display: block;
text-align: center;

View File

@@ -1,52 +1,67 @@
<template>
<div class="search">
<Card>
<Row class="operation">
<Button @click="add" type="primary">添加</Button>
<Button @click="delAll">批量删除</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
sortable="custom"
@on-selection-change="changeSelect"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
<Modal
:title="modalTitle"
v-model="modalVisible"
:mask-closable="false"
:width="500"
>
<Form ref="form" :model="form" :label-width="100" :rules="formValidate">
<FormItem label="计量单位" prop="name">
<Input v-model="form.name" clearable style="width: 100%" />
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit"
>提交</Button
>
<el-card>
<div class="operation">
<el-button type="primary" @click="add">添加</el-button>
<el-button @click="delAll">批量删除</el-button>
</div>
</Modal>
<el-table
ref="table"
v-loading="loading"
border
:data="data"
style="width: 100%"
row-key="id"
@selection-change="changeSelect"
>
<el-table-column type="selection" width="60" align="center" />
<el-table-column prop="name" label="计量单位" min-width="120" />
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column prop="updateTime" label="更新时间" width="180" />
<el-table-column prop="createBy" label="操作人" min-width="150" />
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<template v-if="row">
<a class="link-text" @click="edit(row)">修改</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
<el-form-item label="计量单位" prop="name">
<el-input v-model="form.name" clearable style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -55,156 +70,64 @@ import {
addGoodsUnit,
getGoodsUnitPage,
updateGoodsUnit,
delGoodsUnit
delGoodsUnit,
} from "@/api/index";
import { regular } from "@/utils";
export default {
name: "goods-unit",
data() {
return {
loading: true, // 表单加载状态
modalVisible: false, // 添加或编辑显示
modalTitle: "", // 添加或编辑标题
loading: true,
modalVisible: false,
modalTitle: "",
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
name: "",
},
form: {
// 添加或编辑表单对象初始化数据
name: "",
},
// 表单验证规则
formValidate: {
name: [
regular.REQUIRED,
regular.VARCHAR5
]
name: [regular.REQUIRED, regular.VARCHAR5],
},
submitLoading: false, // 添加或编辑提交状态
selectList: [], // 多选数据
selectCount: 0, // 多选计数
columns: [
// 表头
{
type: "selection",
width: 60,
align: "center",
},
{
title: "计量单位",
key: "name",
minWidth: 120
},
{
title: "创建时间",
key: "createTime",
width: 180
},
{
title: "更新时间",
key: "updateTime",
width: 180
},
{
title: "操作人",
key: "createBy",
minWidth: 150
},
{
title: "操作",
key: "action",
align: "center",
fixed: "right",
width: 200,
render: (h, params) => {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.edit(params.row);
},
},
},
"修改"
),
h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.remove(params.row);
},
},
},
"删除"
),
]);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
submitLoading: false,
selectList: [],
selectCount: 0,
data: [],
total: 0,
};
},
methods: {
// 初始化数据
init() {
this.getDataList();
},
// 分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
this.clearSelectAll();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
// 清除选中
clearSelectAll() {
this.$refs.table.selectAll(false);
this.$refs.table?.clearSelection();
},
// 选中回调
changeSelect(e) {
this.selectList = e;
this.selectCount = e.length;
},
// 获取列表数据
getDataList() {
this.loading = true;
getGoodsUnitPage(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
@@ -215,20 +138,16 @@ export default {
this.total = this.data.length;
this.loading = false;
},
// 修改后提交
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
if (this.modalTitle == "添加") {
if(this.data.find(item=>item.name == this.form.name)){
this.$Message.error('请勿添加重复计量单位!')
this.submitLoading = false
return
if (this.data.find((item) => item.name == this.form.name)) {
this.$Message.error("请勿添加重复计量单位!");
this.submitLoading = false;
return;
}
// 添加 避免编辑后传入id等数据 记得删除
delete this.form.id;
addGoodsUnit(this.form).then((res) => {
this.submitLoading = false;
@@ -239,7 +158,6 @@ export default {
}
});
} else {
// 编辑
updateGoodsUnit(this.id, this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
@@ -252,30 +170,24 @@ export default {
}
});
},
// 添加
add() {
this.modalTitle = "添加";
this.form = {};
this.$refs.form.resetFields();
this.$refs.form?.resetFields();
this.modalVisible = true;
},
// 编辑
edit(v) {
this.id = v.id;
this.modalTitle = "修改";
this.modalVisible = true;
this.form.name = v.name;
},
// 删除
remove(v) {
this.$Modal.confirm({
title: "确认删除",
// 记得确认修改此处
content: "您确认要删除 " + v.name + " ?",
loading: true,
onOk: () => {
// 删除
delGoodsUnit(v.id).then((res) => {
this.$Modal.remove();
if (res.success) {
@@ -286,8 +198,6 @@ export default {
},
});
},
// 全部删除
delAll() {
if (this.selectCount <= 0) {
this.$Message.warning("您还未选择要删除的数据");
@@ -303,7 +213,6 @@ export default {
ids += e.id + ",";
});
ids = ids.substring(0, ids.length - 1);
// 批量删除
delGoodsUnit(ids).then((res) => {
this.$Modal.remove();
if (res.success) {
@@ -312,9 +221,9 @@ export default {
this.getDataList();
}
});
}
},
});
}
},
},
mounted() {
this.init();

View File

@@ -1,253 +1,289 @@
<template>
<div class="search">
<Card>
<Form
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
:label-width="70"
label-width="70px"
class="search-form"
@keydown.enter.native="handleSearch"
@keyup.enter="handleSearch"
>
<Form-item label="商品名称" prop="goodsName">
<Input
type="text"
<el-form-item label="商品名称" prop="goodsName">
<el-input
v-model="searchForm.goodsName"
placeholder="请输入商品名称"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="商品编号" prop="id">
<Input
type="text"
</el-form-item>
<el-form-item label="商品编号" prop="id">
<el-input
v-model="searchForm.id"
placeholder="请输入商品编号"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="店铺名称" prop="id">
<Input
type="text"
</el-form-item>
<el-form-item label="店铺名称" prop="storeName">
<el-input
v-model="searchForm.storeName"
placeholder="请输入店铺名称"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="销售模式" prop="status">
<Select
</el-form-item>
<el-form-item label="销售模式" prop="salesModel">
<el-select
v-model="searchForm.salesModel"
placeholder="请选择"
clearable
style="width: 240px"
>
<Option value="RETAIL">零售</Option>
<Option value="WHOLESALE">批发</Option>
</Select>
</Form-item>
<Form-item label="商品类型" prop="status">
<Select
<el-option label="零售" value="RETAIL" />
<el-option label="批发" value="WHOLESALE" />
</el-select>
</el-form-item>
<el-form-item label="商品类型" prop="goodsType">
<el-select
v-model="searchForm.goodsType"
placeholder="请选择"
clearable
style="width: 240px"
>
<Option value="PHYSICAL_GOODS">实物商品</Option>
<Option value="VIRTUAL_GOODS">虚拟商品</Option>
</Select>
</Form-item>
<Form-item label="商品分组" prop="groupId">
<Select
<el-option label="实物商品" value="PHYSICAL_GOODS" />
<el-option label="虚拟商品" value="VIRTUAL_GOODS" />
</el-select>
</el-form-item>
<el-form-item label="商品分组" prop="groupId">
<el-select
v-model="searchForm.groupId"
placeholder="请选择商品分组"
clearable
filterable
style="width: 240px"
>
<Option v-for="item in goodsGroupList" :value="item.id" :key="item.id">{{ item.groupName }}</Option>
</Select>
</Form-item>
<Button
@click="handleSearch"
class="search-btn"
type="primary"
icon="ios-search"
>搜索</Button
>
</Form>
</Card>
<Card>
<el-option
v-for="item in goodsGroupList"
:key="item.id"
:label="item.groupName"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<div class="goods-tab">
<Tabs v-model="currentStatus" @on-click="goodsStatusClick">
<TabPane v-for="(item,index) in goodsStatusWithCount" :key="index" :label="item.title" :name="item.value">
</TabPane>
</Tabs>
<el-tabs v-model="currentStatus" @tab-click="onStatusTabClick">
<el-tab-pane
v-for="(item, index) in goodsStatusWithCount"
:key="index"
:label="item.title"
:name="item.value"
/>
</el-tabs>
</div>
<!-- 批量操作按钮 -->
<div class="batch-operations" style="margin: 10px 0;">
<Button
<div class="batch-operations" style="margin: 10px 0">
<el-button
type="primary"
:disabled="selectedRows.length === 0"
style="margin-right: 10px"
@click="openSetGoodsGroup"
style="margin-right: 10px;"
>
批量设置分组
</Button>
<Button
</el-button>
<el-button
type="success"
:disabled="selectedRows.length === 0"
style="margin-right: 10px"
@click="batchUpper"
style="margin-right: 10px;"
>
批量上架
</Button>
<Button
</el-button>
<el-button
type="warning"
:disabled="selectedRows.length === 0"
style="margin-right: 10px"
@click="batchLower"
style="margin-right: 10px;"
>
批量下架
</Button>
<Button
</el-button>
<el-button
v-if="currentStatus === 'TOBEAUDITED'"
type="primary"
:disabled="selectedRows.length === 0"
@click="batchAudit"
>
批量审核
</Button>
</el-button>
</div>
<Table
:loading="loading"
:columns="columns"
:data="data"
<el-table
ref="table"
v-loading="loading"
:data="data"
class="mt_10"
@on-select="onSelect"
@on-select-all="onSelectAll"
@on-selection-change="onSelectionChange"
row-key="id"
@selection-change="onSelectionChange"
>
<!-- 商品图片格式化 -->
<template slot="imageSlot" slot-scope="{ row }">
<div style="margin-top: 5px;">
<el-table-column type="selection" width="100" align="center" />
<el-table-column prop="id" label="商品ID" width="200" show-overflow-tooltip />
<el-table-column label="商品图片" width="100" align="center">
<template #default="{ row }">
<img
v-if="row && row.original"
:src="row.original"
style="height: 50px; width: 50px; object-fit: cover;"
style="height: 50px; width: 50px; object-fit: cover"
/>
</template>
</el-table-column>
<el-table-column label="商品名称" min-width="200" show-overflow-tooltip>
<template #default="{ row }">
<a class="link-text" @click="linkTo(row.id, row.skuId)">{{ row.goodsName }}</a>
</template>
</el-table-column>
<el-table-column label="价格" width="200">
<template #default="{ row }">
<span :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, '¥') }}</span>
</template>
</el-table-column>
<el-table-column prop="buyCount" label="销量" width="150" />
<el-table-column prop="quantity" label="库存" width="150" />
<el-table-column label="销售模式" width="150">
<template #default="{ row }">
<span v-if="row">{{ salesModelText(row.salesModel) }}</span>
</template>
</el-table-column>
<el-table-column label="商品类型" width="150">
<template #default="{ row }">
<span v-if="row">{{ goodsTypeText(row.goodsType) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" width="150">
<template #default="{ row }">
<span v-if="row">{{ marketEnableText(row.marketEnable) }}</span>
</template>
</el-table-column>
<el-table-column label="审核状态" width="150">
<template #default="{ row }">
<span v-if="row">{{ authFlagText(row.authFlag) }}</span>
</template>
</el-table-column>
<el-table-column prop="storeName" label="店铺名称" width="200" show-overflow-tooltip />
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<template v-if="row.authFlag === 'TOBEAUDITED'">
<a class="link-text" @click="openAuditModal(row)">审核</a>
<span class="op-split">|</span>
<a class="link-text" @click="showDetail(row)">查看</a>
</template>
<template v-else-if="row.marketEnable === 'DOWN'">
<a class="link-text" @click="upper(row)">上架</a>
<span class="op-split">|</span>
<a class="link-text" @click="showDetail(row)">查看</a>
</template>
<template v-else>
<a class="link-text" @click="edit(row)">下架</a>
<span class="op-split">|</span>
<a class="link-text" @click="showDetail(row)">查看</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</template>
</el-card>
<!-- 商品栏目格式化 -->
<template slot="goodsSlot" slot-scope="{ row }">
<div style="margin: 5px 0px; padding: 10px 0px;">
<div class="div-zoom">
<a @click="linkTo(row.id, row.skuId)">{{ row.goodsName }}</a>
</div>
</div>
<el-dialog v-model="modalVisible" title="下架操作" width="500px" :close-on-click-modal="false">
<el-form ref="underForm" :model="underForm" label-width="100px">
<el-form-item label="下架原因" prop="reason">
<el-input v-model="underForm.reason" clearable />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="lower">提交</el-button>
</template>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
<Modal
title="下架操作"
v-model="modalVisible"
:mask-closable="false"
:width="500"
>
<Form ref="underForm" :model="underForm" :label-width="100">
<FormItem label="下架原因" prop="reason">
<Input v-model="underForm.reason" clearable style="width: 100%" />
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="lower"
>提交</Button
>
</div>
</Modal>
<Modal
title="商品审核"
v-model="auditModalVisible"
:mask-closable="false"
:width="500"
>
<Form ref="auditForm" :model="goodsAuditForm" :label-width="100">
<FormItem label="审核结果" prop="auth_flag">
<RadioGroup v-model="goodsAuditForm.auth_flag">
<Radio :label="1">审核通过</Radio>
<Radio :label="2">审核拒绝</Radio>
</RadioGroup>
</FormItem>
<!-- <FormItem label="审核备注" prop="reason" v-if="goodsAuditForm.auth_flag === 2">
<Input v-model="goodsAuditForm.reason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
</FormItem> -->
</Form>
<div slot="footer">
<Button type="text" @click="auditModalVisible = false">取消</Button>
<Button type="primary" @click="confirmAudit">提交审核</Button>
</div>
</Modal>
<!-- 批量审核弹框 -->
<Modal
title="批量商品审核"
</el-dialog>
<el-dialog v-model="auditModalVisible" title="商品审核" width="500px" :close-on-click-modal="false">
<el-form ref="auditForm" :model="goodsAuditForm" label-width="100px">
<el-form-item label="审核结果" prop="auth_flag">
<el-radio-group v-model="goodsAuditForm.auth_flag">
<el-radio :value="1">审核通过</el-radio>
<el-radio :value="2">审核拒绝</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="auditModalVisible = false">取消</el-button>
<el-button type="primary" @click="confirmAudit">提交审核</el-button>
</template>
</el-dialog>
<el-dialog
v-model="batchAuditModalVisible"
:mask-closable="false"
:width="500"
title="批量商品审核"
width="500px"
:close-on-click-modal="false"
>
<Form ref="batchAuditForm" :model="batchAuditForm" :label-width="100">
<FormItem label="审核结果" prop="auth_flag">
<RadioGroup v-model="batchAuditForm.auth_flag">
<Radio :label="1">审核通过</Radio>
<Radio :label="2">审核拒绝</Radio>
</RadioGroup>
</FormItem>
<FormItem label="审核备注" prop="reason" v-if="batchAuditForm.auth_flag === 2">
<Input v-model="batchAuditForm.reason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
</FormItem>
<FormItem label="选中商品">
<div style="max-height: 200px; overflow-y: auto;">
<Tag v-for="item in selectedRows" :key="item.id" style="margin: 2px;">{{item.goodsName}}</Tag>
<el-form ref="batchAuditForm" :model="batchAuditForm" label-width="100px">
<el-form-item label="审核结果" prop="auth_flag">
<el-radio-group v-model="batchAuditForm.auth_flag">
<el-radio :value="1">审核通过</el-radio>
<el-radio :value="2">审核拒绝</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="batchAuditForm.auth_flag === 2" label="审核备注" prop="reason">
<el-input v-model="batchAuditForm.reason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
</el-form-item>
<el-form-item label="选中商品">
<div style="max-height: 200px; overflow-y: auto">
<el-tag v-for="item in selectedRows" :key="item.id" style="margin: 2px">{{ item.goodsName }}</el-tag>
</div>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="batchAuditModalVisible = false">取消</Button>
<Button type="primary" @click="submitBatchAudit">提交审核</Button>
</div>
</Modal>
<Modal v-model="goodsGroupFlag" title="批量设置商品分组" width="420">
<Form ref="goodsGroupForm" :model="goodsGroupForm" :rules="goodsGroupRule" :label-width="90">
<FormItem label="商品分组" prop="groupId">
<Select v-model="goodsGroupForm.groupId" clearable filterable style="width: 240px">
<Option v-for="item in goodsGroupList" :value="item.id" :key="item.id">{{ item.groupName }}</Option>
</Select>
</FormItem>
</Form>
<div slot="footer">
<Button @click="goodsGroupFlag = false">取消</Button>
<Button type="primary" :loading="goodsGroupLoading" @click="submitSetGoodsGroup">确定</Button>
</div>
</Modal>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="batchAuditModalVisible = false">取消</el-button>
<el-button type="primary" @click="submitBatchAudit">提交审核</el-button>
</template>
</el-dialog>
<el-dialog v-model="goodsGroupFlag" title="批量设置商品分组" width="420px">
<el-form ref="goodsGroupForm" :model="goodsGroupForm" :rules="goodsGroupRule" label-width="90px">
<el-form-item label="商品分组" prop="groupId">
<el-select v-model="goodsGroupForm.groupId" clearable filterable style="width: 240px">
<el-option
v-for="item in goodsGroupList"
:key="item.id"
:label="item.groupName"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="goodsGroupFlag = false">取消</el-button>
<el-button type="primary" :loading="goodsGroupLoading" @click="submitSetGoodsGroup">确定</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -261,11 +297,7 @@ import {
getGoodsGroupByPage,
addGoodsGroupItems
} from "@/api/goods";
import vueQr from "vue-qr";
export default {
components: {
"vue-qr": vueQr,
},
name: "goods",
data() {
return {
@@ -291,259 +323,10 @@ export default {
auditModalVisible: false, // 审核弹框显示状态
currentAuditGoods: null, // 当前审核的商品
submitLoading: false, // 添加或编辑提交状态
columns: [
{
type: 'selection',
width: 60,
align: 'center'
},
{
title: "商品ID",
key: "id",
width: 180,
tooltip: true,
},
{
title: "商品图片",
key: "original",
width: 180,
slot: "imageSlot",
},
{
title: "商品名称",
key: "goodsName",
minWidth: 180,
slot: "goodsSlot",
},
{
title: "价格",
key: "price",
width: 100,
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.price,color:this.$mainColor}} );
},
},
{
title: "销量",
key: "buyCount",
width: 100,
render: (h, params) => {
return h("span", params.row.buyCount || 0);
},
},
{
title: "库存",
key: "quantity",
width: 100,
render: (h, params) => {
return h("span", params.row.quantity || 0);
},
},
{
title: "销售模式",
key: "salesModel",
width: 100,
render: (h, params) => {
if (params.row.salesModel === "RETAIL") {
return h("Tag", { props: { color: "orange" } }, "零售");
} else if (params.row.salesModel === "WHOLESALE") {
return h("Tag", { props: { color: "magenta" } }, "批发");
} else {
return h("Tag", { props: { color: "volcano" } }, "其他类型");
}
},
},
{
title: "商品类型",
key: "goodsType",
width: 120,
render: (h, params) => {
if (params.row.goodsType === "PHYSICAL_GOODS") {
return h("Tag", { props: { color: "green" } }, "实物商品");
} else if (params.row.goodsType === "VIRTUAL_GOODS") {
return h("Tag", { props: { color: "volcano" } }, "虚拟商品");
} else {
return h("Tag", { props: { color: "geekblue" } }, "电子卡券");
}
},
},
{
title: "状态",
key: "marketEnable",
width: 120,
render: (h, params) => {
if (params.row.marketEnable == "DOWN") {
return h("Tag", { props: { color: "volcano" } }, "下架");
} else if (params.row.marketEnable == "UPPER") {
return h("Tag", { props: { color: "green" } }, "上架");
}
},
},
{
title: "审核状态",
key: "authFlag",
width: 180,
render: (h, params) => {
if (params.row.authFlag == "TOBEAUDITED") {
return h("Tag", { props: { color: "volcano" } }, "待审核");
} else if (params.row.authFlag == "PASS") {
return h("Tag", { props: { color: "green" } }, "通过");
} else if (params.row.authFlag == "REFUSE") {
return h("Tag", { props: { color: "red" } }, "拒绝");
}
},
},
{
title: "店铺名称",
key: "storeName",
width: 180, // 使用minWidth替代width
tooltip: true,
},
{
title: "操作",
key: "action",
align: "center",
fixed: "right",
width: 200,
render: (h, params) => {
// 如果是待审核状态,显示审核按钮
if (params.row.authFlag === "TOBEAUDITED") {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none"
},
on: {
click: () => {
this.openAuditModal(params.row);
},
},
},
"审核"
),
h("span", {
style: {
margin: "0 8px",
color: "#dcdee2"
}
}, "|"),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none"
},
on: {
click: () => {
this.showDetail(params.row);
},
},
},
"查看"
),
]);
}
// 原有的上架/下架逻辑
else if (params.row.marketEnable == "DOWN") {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none"
},
on: {
click: () => {
this.upper(params.row);
},
},
},
"上架"
),
h("span", {
style: {
margin: "0 8px",
color: "#dcdee2"
}
}, "|"),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none"
},
on: {
click: () => {
this.showDetail(params.row);
},
},
},
"查看"
),
]);
} else {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none"
},
on: {
click: () => {
this.edit(params.row);
},
},
},
"下架"
),
h("span", {
style: {
margin: "0 8px",
color: "#dcdee2"
}
}, "|"),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none"
},
on: {
click: () => {
this.showDetail(params.row);
},
},
},
"查看"
),
]);
}
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
currentStatus: '',
currentStatus: "ALL",
goodsNumerData: {},
goodsAuditForm: {
// 商品编辑表单
auth_flag: 1,
},
selectedRows: [], // 选中的行数据
selectAll: false, // 全选状态
batchAuditModalVisible: false, // 批量审核弹框显示状态
@@ -565,7 +348,7 @@ export default {
computed: {
goodsStatusWithCount() {
return [
{title: '全部', value: ''},
{title: '全部', value: 'ALL'},
{title: `出售中${this.goodsNumerData.upperGoodsNum ? '(' + this.goodsNumerData.upperGoodsNum + ')' : ''}`, value: 'UPPER'},
{title: `仓库中${this.goodsNumerData.downGoodsNum ? '(' + this.goodsNumerData.downGoodsNum + ')' : ''}`, value: 'DOWN'},
{title: `待审核${this.goodsNumerData.auditGoodsNum ? '(' + this.goodsNumerData.auditGoodsNum + ')' : ''}`, value: 'TOBEAUDITED'},
@@ -574,6 +357,33 @@ export default {
}
},
methods: {
clearTableSelection() {
this.$refs.table?.clearSelection?.();
},
onStatusTabClick(tab) {
this.goodsStatusClick(tab.paneName);
},
salesModelText(v) {
if (v === "RETAIL") return "零售";
if (v === "WHOLESALE") return "批发";
return "其他类型";
},
goodsTypeText(v) {
if (v === "PHYSICAL_GOODS") return "实物商品";
if (v === "VIRTUAL_GOODS") return "虚拟商品";
return "电子卡券";
},
marketEnableText(v) {
if (v === "DOWN") return "下架";
if (v === "UPPER") return "上架";
return "";
},
authFlagText(v) {
if (v === "TOBEAUDITED") return "待审核";
if (v === "PASS") return "通过";
if (v === "REFUSE") return "拒绝";
return "";
},
// 初始化数据
init() {
this.getDataList();
@@ -667,31 +477,23 @@ export default {
//查看商品详情
showDetail(v) {
let id = v.id;
this.$options.filters.customRouterPush({
this.$filters.customRouterPush({
name: "goods-detail",
query: { id: id },
})
query: { id: v.id },
});
},
// 商品状态筛选
goodsStatusClick(item) {
// 根据选择的状态设置搜索条件
if (item === 0) {
// 全部:清除状态筛选
goodsStatusClick(name) {
if (name === "ALL" || name === "" || name === undefined || name === null) {
delete this.searchForm.goodsStatus;
this.currentStatus = "ALL";
} else {
// 其他状态正常赋值
this.searchForm.goodsStatus = item;
this.searchForm.goodsStatus = name;
this.currentStatus = name;
}
this.currentStatus = item;
// tab切换时清除选中内容
this.selectedRows = [];
if (this.$refs.table) {
this.$refs.table.selectAll(false);
}
this.clearTableSelection();
this.getDataList();
},
examine(v, authFlag) {
@@ -762,15 +564,6 @@ export default {
});
},
// 选择框事件处理
onSelect(selection, row) {
// 单行选择时触发
},
onSelectAll(selection) {
// 全选时触发
},
onSelectionChange(selection) {
this.selectedRows = selection;
},
@@ -939,9 +732,7 @@ export default {
this.goodsGroupFlag = false;
this.selectedRows = [];
this.selectAll = false;
if (this.$refs.table && this.$refs.table.selectAll) {
this.$refs.table.selectAll(false);
}
this.clearTableSelection();
this.getDataList();
} else if (res && res.message) {
this.$Message.error(res.message);
@@ -961,7 +752,7 @@ export default {
<style lang="scss" scoped>
// Tab组件样式
.goods-tab {
::v-deep .ivu-tabs-tab {
:deep(.el-tabs__item) {
font-size: 14px;
}
}

View File

@@ -1,79 +1,93 @@
<template>
<div class="search">
<Card>
<Form
<el-card>
<el-form
ref="searchForm"
@keydown.enter.native="handleSearch"
:model="searchForm"
inline
:label-width="70"
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<Form-item label="商品名称" prop="goodsName">
<Input
type="text"
<el-form-item label="商品名称" prop="goodsName">
<el-input
v-model="searchForm.goodsName"
placeholder="请输入商品名称"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="商品编号" prop="id">
<Input
type="text"
</el-form-item>
<el-form-item label="商品编号" prop="id">
<el-input
v-model="searchForm.id"
placeholder="请输入商品编号"
clearable
style="width: 240px"
/>
</Form-item>
<Button @click="handleSearch" class="search-btn" type="primary" icon="ios-search"
>搜索</Button
>
</Form>
</Card>
<Card>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
class="mt_10"
>
<!-- 商品栏目格式化 -->
<template slot="goodsSlot" slot-scope="scope">
<div style="margin-top: 5px; height: 80px; display: flex">
<div style="">
<img
:src="scope.row.original"
style="height: 60px; margin-top: 3px; width: 60px"
/>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<el-table v-loading="loading" :data="data" ref="table" class="mt_10" style="width: 100%">
<el-table-column label="商品名称" min-width="200" show-overflow-tooltip>
<template #default="{ row }">
<div v-if="row" style="margin-top: 5px; height: 80px; display: flex; align-items: flex-start">
<img
v-if="row.original"
:src="row.original"
style="height: 60px; width: 60px; object-fit: cover; margin-top: 3px"
alt=""
/>
<div style="margin-left: 13px">
<div class="div-zoom">
<a>{{ scope.row.goodsName }}</a>
<a class="link-text">{{ row.goodsName }}</a>
</div>
</div>
</div>
</template>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
</el-table-column>
<el-table-column prop="id" label="商品编号" min-width="120" show-overflow-tooltip />
<el-table-column label="价格" min-width="120">
<template #default="{ row }">
<span v-if="row" :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, "") }}</span>
</template>
</el-table-column>
<el-table-column label="审核状态" min-width="120">
<template #default="{ row }">
<el-tag v-if="row" :type="authFlagTagType(row.authFlag)">{{ authFlagText(row.authFlag) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="storeName" label="店铺名称" min-width="120" show-overflow-tooltip />
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<template v-if="row">
<a class="link-text" @click="examine(row, 1)">通过</a>
<span class="op-split">|</span>
<a class="link-text" @click="examine(row, 2)">拒绝</a>
<span class="op-split">|</span>
<a class="link-text" @click="showDetail(row)">查看</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</div>
</template>
@@ -81,214 +95,82 @@
import { authGoods, getAuthGoodsListData } from "@/api/goods";
export default {
name: "goods",
components: {},
name: "goodsApply",
data() {
return {
id: "", //要操作的id
loading: true, // 表单加载状态
loading: true,
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "create_time", // 默认排序字段
order: "desc", // 默认排序方式
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
},
goodsAuditForm: {
// 商品编辑表单
auth_flag: 1,
},
columns: [
{
title: "商品名称",
key: "goodsName",
minWidth: 180,
slot: "goodsSlot",
tooltip: true,
},
{
title: "商品编号",
key: "id",
minWidth: 100,
tooltip: true,
},
{
title: "价格",
key: "price",
minWidth: 130,
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.price,color:this.$mainColor}} );
},
},
{
title: "审核状态",
key: "authFlag",
minWidth: 130,
render: (h, params) => {
if (params.row.authFlag == "TOBEAUDITED") {
return h("div", [
h("Badge", {
props: {
status: "error",
text: "待审核",
},
}),
]);
} else if (params.row.authFlag == "PASS") {
return h("div", [
h("Badge", {
props: {
status: "success",
text: "审核通过",
},
}),
]);
} else if (params.row.authFlag == "REFUSE") {
return h("div", [
h("Badge", {
props: {
status: "error",
text: "审核拒绝",
},
}),
]);
}
},
},
{
title: "店铺名称",
key: "storeName",
minWidth: 100,
tooltip: true,
},
{
title: "操作",
key: "action",
align: "center",
fixed: "right",
width: 200,
render: (h, params) => {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
marginRight: "5px",
},
on: {
click: () => {
this.examine(params.row, 1);
},
},
},
"通过"
),
h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
marginRight: "5px",
},
on: {
click: () => {
this.examine(params.row, 2);
},
},
},
"拒绝"
),
h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none"
},
on: {
click: () => {
this.showDetail(params.row);
},
},
},
"查看"
),
]);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
data: [],
total: 0,
};
},
methods: {
authFlagText(v) {
const map = {
TOBEAUDITED: "待审核",
PASS: "审核通过",
REFUSE: "审核拒绝",
};
return map[v] || v || "-";
},
authFlagTagType(v) {
const map = {
TOBEAUDITED: "warning",
PASS: "success",
REFUSE: "danger",
};
return map[v] || "info";
},
init() {
// 初始化数据
this.getDataList();
},
changePage(v) {
// 改变页码
this.searchForm.pageNumber = v;
this.getDataList();
},
changePageSize(v) {
// 改变每页数量
this.searchForm.pageSize = v;
this.getDataList();
},
handleSearch() {
// 搜索
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
getDataList() {
// 获取列表数据
this.loading = true;
// 带多条件搜索参数获取表单数据
this.searchForm.authFlag = 0;
getAuthGoodsListData(this.searchForm).then((res) => {
getAuthGoodsListData(this.searchForm)
.then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
})
.catch(() => {
this.loading = false;
});
},
examine(v, authFlag) {
// 审核商品
let examine = "通过";
this.goodsAuditForm.authFlag = "PASS";
if (authFlag != 1) {
examine = "拒绝";
this.goodsAuditForm.authFlag = "REFUSE";
}
const examine = authFlag === 1 ? "通过" : "拒绝";
this.goodsAuditForm.authFlag = authFlag === 1 ? "PASS" : "REFUSE";
this.$Modal.confirm({
title: "确认审核",
content: "您确认要审核" + examine + " " + v.goodsName + " ?",
content: `您确认要审核${examine} ${v.goodsName} ?`,
loading: true,
onOk: () => {
let formData = new FormData();
formData.append('goodsIds', v.id);
formData.append('authFlag', this.goodsAuditForm.authFlag);
const formData = new FormData();
formData.append("goodsIds", v.id);
formData.append("authFlag", this.goodsAuditForm.authFlag);
authGoods(formData).then((res) => {
this.$Modal.remove();
if (res.success) {
@@ -299,13 +181,11 @@ export default {
},
});
},
//查看商品详情
showDetail(v) {
let id = v.id;
this.$options.filters.customRouterPush({
this.$filters.customRouterPush({
name: "goods-detail",
query: { id: id },
})
query: { id: v.id },
});
},
},
mounted() {
@@ -313,3 +193,20 @@ export default {
},
};
</script>
<style scoped>
.link-text {
color: #409eff;
cursor: pointer;
text-decoration: none;
}
.op-split {
margin: 0 8px;
color: #dcdee2;
}
.div-zoom {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

View File

@@ -1,247 +1,159 @@
<template>
<div>
<Form :label-width="120">
<Card>
<el-form label-width="120px">
<el-card>
<div class="base-info-item">
<h4>基本信息</h4>
<div class="form-item-view">
<FormItem label="商品分类">
<el-form-item label="商品分类">
<span v-for="(item, index) in goods.categoryName" :key="index">
{{ item }}
<i v-if="index !== goods.categoryName.length - 1">&gt;</i>
</span>
</FormItem>
<FormItem label="商品名称">
{{ goods.goodsName }}
</FormItem>
<FormItem label="商品卖点">
{{ goods.sellingPoint }}
</FormItem>
<FormItem label="商品参数">
<div v-if="goods.goodsParamsDTOList && goods.goodsParamsDTOList.length" v-for="(item,index) in goods.goodsParamsDTOList" :key="index">
<div style="margin-bottom: 10px; display: flex; align-items: center;" >
{{ item.groupName }} : <tag v-for="(child,i) in item.goodsParamsItemDTOList" :key="i">
</el-form-item>
<el-form-item label="商品名称">{{ goods.goodsName }}</el-form-item>
<el-form-item label="商品卖点">{{ goods.sellingPoint }}</el-form-item>
<el-form-item label="商品参数">
<div
v-if="goods.goodsParamsDTOList && goods.goodsParamsDTOList.length"
v-for="(item, index) in goods.goodsParamsDTOList"
:key="index"
style="margin-bottom: 10px; display: flex; align-items: center"
>
{{ item.groupName }} :
<el-tag v-for="(child, i) in item.goodsParamsItemDTOList" :key="i" style="margin-left: 4px">
{{ child.paramName }} - {{ child.paramValue }}
</tag>
</el-tag>
</div>
</div>
</FormItem>
</el-form-item>
</div>
<h4>商品交易信息</h4>
<div class="form-item-view">
<FormItem label="计量单位"> {{ goods.goodsUnit }}</FormItem>
<FormItem label="销售模式">
<el-form-item label="计量单位">{{ goods.goodsUnit }}</el-form-item>
<el-form-item label="销售模式">
{{ goods.salesModel === "RETAIL" ? "零售型" : "批发型" }}
</FormItem>
<FormItem label="销售规则" v-if="goods.salesModel !== 'RETAIL'">
<Table
border
:columns="wholesalePreviewColumns"
:data="wholesaleData"
>
</Table>
</FormItem>
</el-form-item>
<el-form-item v-if="goods.salesModel !== 'RETAIL'" label="销售规则">
<el-table border :data="wholesaleData" style="width: 100%">
<el-table-column label="销售规则" width="300">
<template #default="{ row }">
<span v-if="row">
当商品购买数量 {{ row.num }} 售价为 {{ row.price }} /{{ goods.goodsUnit }}</span>
</template>
</el-table-column>
</el-table>
</el-form-item>
</div>
<h4>商品规格及图片</h4>
<div class="form-item-view">
<FormItem label="商品编号"> {{ goods.id }}</FormItem>
<FormItem label="商品价格">
<priceColorScheme :value="goods.price" :color="$mainColor"></priceColorScheme>
</FormItem>
<FormItem label="商品图片">
<el-form-item label="商品编号">{{ goods.id }}</el-form-item>
<el-form-item label="商品价格">
<priceColorScheme :value="goods.price" :color="$mainColor" />
</el-form-item>
<el-form-item label="商品图片">
<div
class="demo-upload-list"
v-for="(item, __index) in goods.goodsGalleryList"
:key="__index"
class="demo-upload-list"
>
<img :src="item" />
<div class="demo-upload-list-cover">
<Icon
type="ios-eye-outline"
@click.native="handleViewGoodsPicture(item)"
></Icon>
<el-icon class="preview-icon" @click="handleViewGoodsPicture(item)"><View /></el-icon>
</div>
<Modal title="View Image" v-model="goodsPictureVisible">
<img
:src="previewGoodsPicture"
v-if="goodsPictureVisible"
style="width: 100%"
/>
</Modal>
</div>
</FormItem>
<FormItem label="商品视频">
<video
v-if="goods.goodsVideo"
controls
class="player"
:src="goods.goodsVideo"
/>
</FormItem>
<FormItem label="商品规格">
<Table :columns="skuColumn" :data="skuData">
<template slot="showImage" slot-scope="scope">
<div style="margin-top: 5px; display: flex">
<div>
<el-dialog v-model="goodsPictureVisible" title="View Image" width="600px">
<img v-if="goodsPictureVisible" :src="previewGoodsPicture" style="width: 100%" />
</el-dialog>
</el-form-item>
<el-form-item label="商品视频">
<video v-if="goods.goodsVideo" controls class="player" :src="goods.goodsVideo" />
</el-form-item>
<el-form-item label="商品规格">
<el-table :data="skuData" style="width: 100%">
<el-table-column prop="specs" label="规格" />
<el-table-column prop="sn" label="编号" />
<el-table-column prop="weight" label="重量(kg)" />
<el-table-column
v-for="(item, index) in wholesaleData"
:key="'wp' + index"
:label="'购买量 ≥ ' + item.num"
>
<template #default>
<el-input v-if="wholesaleData[index]" v-model="wholesaleData[index].price" disabled>
<template #append></template>
</el-input>
</template>
</el-table-column>
<el-table-column v-if="goods.salesModel !== 'WHOLESALE'" label="价格">
<template #default="{ row }">
<priceColorScheme v-if="row" :value="row.price" :color="$mainColor" />
</template>
</el-table-column>
<el-table-column v-if="goods.salesModel !== 'WHOLESALE'" prop="quantity" label="库存" />
<el-table-column label="图片">
<template #default="{ row }">
<div v-if="row" style="margin-top: 5px; display: flex">
<img
v-for="(item,index) in scope.row.image"
v-for="(item, index) in row.image"
:key="index"
:src="item"
style="height: 60px; margin: 10px; width: 60px"
/>
</div>
</div>
</template>
<template slot-scope="{ row }" slot="wholePrice0">
<Input
v-if="wholesaleData[0]"
clearable
disabled
v-model="wholesaleData[0].price"
>
<span slot="append"></span>
</Input>
</template>
<template slot-scope="{ row }" slot="wholePrice1">
<Input
v-if="wholesaleData[1]"
clearable
disabled
v-model="wholesaleData[1].price"
>
<span slot="append"></span>
</Input>
</template>
<template slot-scope="{ row }" slot="wholePrice2">
<Input
v-if="wholesaleData[2]"
clearable
disabled
v-model="wholesaleData[2].price"
>
<span slot="append"></span>
</Input>
</template>
</Table>
</FormItem>
</el-table-column>
</el-table>
</el-form-item>
</div>
<h4>商品详情描述</h4>
<div class="form-item-view">
<FormItem label="商品描述">
<el-form-item label="商品描述">
<div v-html="goods.intro"></div>
</FormItem>
<FormItem label="移动端描述">
</el-form-item>
<el-form-item label="移动端描述">
<div v-html="goods.mobileIntro"></div>
</FormItem>
</el-form-item>
</div>
</div>
</Card>
</Form>
</el-card>
</el-form>
</div>
</template>
<script>
import { View } from "@element-plus/icons-vue";
import { getGoodsDetail } from "@/api/goods";
export default {
name: "goodsDetail",
components: { View },
data() {
return {
goods: {}, // 商品信息
previewGoodsPicture: "", // 预览图片
goodsPictureVisible: false, // 预览图片模态框
wholesalePreviewColumns: [
{
title: "销售规则",
width: 300,
render: (h, params) => {
let guide =
"当商品购买数量 ≥" +
params.row.num +
" 时,售价为 ¥" +
params.row.price +
" /" +
this.goods.goodsUnit;
return h("div", guide);
},
},
],
goods: {},
previewGoodsPicture: "",
goodsPictureVisible: false,
wholesaleData: [],
skuColumn: [
// 规格表头
{
title: "规格",
key: "specs",
},
{
title: "编号",
key: "sn",
},
{
title: "重量(kg)",
key: "weight",
},
],
skuData: [], // sku数据
skuData: [],
};
},
mounted() {
this.initGoods(this.$route.query.id);
},
methods: {
// 初始化数据,获取商品详情
initGoods(id) {
getGoodsDetail(id).then((res) => {
this.goods = res.result;
let that = this;
res.result.skuList.forEach(function (sku, index, array) {
that.skuData.push({
this.skuData = res.result.skuList.map((sku) => ({
specs: sku.goodsName,
sn: sku.sn,
weight: sku.weight,
cost: sku.cost,
price: sku.price,
image: sku.goodsGalleryList,
quantity:sku.quantity
});
});
if (res.result.salesModel === "WHOLESALE" && res.result.wholesaleList) {
res.result.wholesaleList.forEach((item, index) => {
this.skuColumn.push({
title: "购买量 ≥ " + item.num,
slot: "wholePrice" + index,
});
});
} else {
this.skuColumn.push(
// {
// title: "成本",
// key: "cost",
// render: (h, params) => {
// console.log(params)
// return h("priceColorScheme", {props:{value:params.row.cost,color:this.$mainColor}} );
// },
// },
{
title: "价格",
key: "price",
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.price,color:this.$mainColor}} );
},
},{
title: "库存",
key: "quantity",
}
);
}
this.skuColumn.push({
title: "图片",
slot: "showImage",
});
this.wholesaleData = res.result.wholesaleList;
quantity: sku.quantity,
}));
this.wholesaleData = res.result.wholesaleList || [];
});
},
// 预览商品图片
handleViewGoodsPicture(url) {
this.previewGoodsPicture = url;
this.goodsPictureVisible = true;
@@ -251,7 +163,6 @@ export default {
</script>
<style lang="scss" soped>
/*平铺*/
div.base-info-item {
h4 {
margin-bottom: 10px;
@@ -300,13 +211,10 @@ div.base-info-item {
.demo-upload-list:hover .demo-upload-list-cover {
display: block;
}
.demo-upload-list-cover i {
.preview-icon {
color: #fff;
font-size: 20px;
cursor: pointer;
margin: 0 2px;
}
.ivu-table table {
width: 100% !important;
margin-top: 20px;
}
</style>

View File

@@ -1,74 +1,148 @@
<template>
<div class="search">
<Card>
<Form ref="searchForm" @submit.native.prevent @keydown.enter.native="handleSearch" :model="searchForm" inline :label-width="70"
class="search-form">
<Form-item label="品牌名称">
<Input type="text" v-model="searchForm.name" placeholder="请输入品牌名称" clearable style="width: 240px"/>
</Form-item>
<Button @click="handleSearch" type="primary">搜索</Button>
</Form>
</Card>
<Card>
<Row class="operation padding-row">
<Button @click="add" type="primary">添加</Button>
</Row>
<Table :loading="loading" border :columns="columns" :data="data" ref="table"></Table>
<Row type="flex" justify="end" class="mt_10">
<Page :current="searchForm.pageNumber" :total="total" :page-size="searchForm.pageSize" @on-change="changePage"
@on-page-size-change="changePageSize" :page-size-opts="[20, 50, 100]" size="small"
show-total show-elevator show-sizer></Page>
</Row>
</Card>
<Modal :title="modalTitle" v-model="modalVisible" :mask-closable="false" :width="500">
<Form ref="form" :model="form" :label-width="100" :rules="formValidate">
<FormItem label="品牌名称" prop="name">
<Input v-model="form.name" clearable style="width: 100%"/>
</FormItem>
<FormItem label="品牌图标" prop="logo">
<div style="display: flex; align-items: center; gap: 12px;">
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<el-form-item label="品牌名称">
<el-input
v-model="searchForm.name"
placeholder="请输入品牌名称"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<div class="operation padding-row">
<el-button type="primary" @click="add">添加</el-button>
</div>
<el-table ref="table" v-loading="loading" border :data="data" style="width: 100%">
<el-table-column prop="name" label="品牌名称" width="200" />
<el-table-column label="品牌图标" align="left" >
<template #default="{ row }">
<img
v-if="row"
:src="row.logo || ''"
alt="加载图片失败"
style="cursor: pointer; width: 80px; height: 60px; margin: 10px 0; object-fit: contain"
/>
</template>
</el-table-column>
<el-table-column label="状态" align="left" width="150">
<template #default="{ row }">
<el-switch
v-if="row"
:model-value="row.deleteFlag == 0"
inline-prompt
active-text="启用"
inactive-text="禁用"
@change="(val) => handleStatusSwitchChange(row, val)"
/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" min-width="170" show-overflow-tooltip />
<el-table-column label="操作" width="230" align="center" fixed="right">
<template #default="{ row }">
<template v-if="row">
<a class="link-text" @click="edit(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="openCategoryModal(row)">关联分类</a>
<span class="op-split">|</span>
<a class="link-text" @click="delBrand(row.id)">删除</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
<el-form-item label="品牌名称" prop="name">
<el-input v-model="form.name" clearable style="width: 100%" />
</el-form-item>
<el-form-item label="品牌图标" prop="logo">
<div style="display: flex; align-items: center; gap: 12px">
<img
:src="form.logo || defaultPic"
alt="品牌图标"
style="width: 80px; height: 60px; object-fit: contain; border: 1px solid #dcdee2; border-radius: 4px; background: #fff;"
style="width: 80px; height: 60px; object-fit: contain; border: 1px solid #dcdee2; border-radius: 4px; background: #fff"
/>
<Button type="text" @click="openLogoPicker">修改</Button>
<el-button type="primary" link @click="openLogoPicker">修改</el-button>
</div>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit">提交</Button>
</div>
</Modal>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
<Modal width="1200px" v-model="picModelFlag" footer-hide>
<ossManage @callback="callbackSelected" :isComponent="true" :initialize="picModelFlag" ref="ossManage" />
</Modal>
<el-dialog v-model="picModelFlag" width="1200px" :show-close="true">
<ossManage
ref="ossManage"
:is-component="true"
:initialize="picModelFlag"
@callback="callbackSelected"
/>
</el-dialog>
<Modal
:title="categoryModalTitle"
<el-dialog
v-model="categoryModalVisible"
:mask-closable="false"
:width="700"
:title="categoryModalTitle"
width="700px"
:close-on-click-modal="false"
>
<div style="position: relative; max-height: 520px; overflow: auto;">
<Spin size="large" fix v-if="categoryTreeLoading"></Spin>
<Tree
<div v-loading="categoryTreeLoading" style="position: relative; max-height: 520px; overflow: auto">
<el-tree
ref="categoryTree"
:key="categoryTreeKey"
:data="categoryTreeData"
:props="{ label: 'title', children: 'children' }"
node-key="id"
show-checkbox
@on-check-change="onCategoryTreeCheckChange"
></Tree>
default-expand-all
:default-checked-keys="selectedCategoryIds"
@check="onCategoryTreeCheckChange"
/>
</div>
<div slot="footer">
<Button type="text" @click="categoryModalVisible = false">取消</Button>
<Button type="primary" :loading="categorySubmitLoading" @click="submitBrandCategory"
>提交</Button
>
</div>
</Modal>
<template #footer>
<el-button @click="categoryModalVisible = false">取消</el-button>
<el-button type="primary" :loading="categorySubmitLoading" @click="submitBrandCategory">
提交
</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -84,22 +158,21 @@ import {
saveBrandCategory,
} from "@/api/goods";
import ossManage from "@/views/sys/oss-manage/ossManage";
import { regular } from "@/utils";
export default {
name: "brand",
components: {
ossManage
ossManage,
},
data() {
return {
defaultPic: require("@/assets/default.png"),
loading: true, // 表单加载状态
modalType: 0, // 添加或编辑标识
modalVisible: false, // 添加或编辑显示
modalTitle: "", // 添加或编辑标题
picModelFlag: false, // 图片选择器
loading: true,
modalType: 0,
modalVisible: false,
modalTitle: "",
picModelFlag: false,
categoryModalVisible: false,
categoryModalTitle: "关联分类",
categoryTreeLoading: false,
@@ -109,173 +182,23 @@ export default {
currentBrandId: "",
selectedCategoryIds: [],
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "create_time", // 默认排序字段
order: "desc", // 默认排序方式
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
},
form: {
// 添加或编辑表单对象初始化数据
name: "",
logo: "",
deleteFlag: "",
},
// 表单验证规则
formValidate: {
name: [
regular.REQUIRED,
regular.VARCHAR20
],
logo: [
regular.REQUIRED,
regular.URL200
],
name: [regular.REQUIRED, regular.VARCHAR20],
logo: [regular.REQUIRED, regular.URL200],
},
submitLoading: false, // 添加或编辑提交状态
columns: [
{
title: "品牌名称",
key: "name",
width: 200,
resizable: true,
sortable: false,
},
{
title: "品牌图标",
key: "logo",
align: "left",
render: (h, params) => {
return h("img", {
attrs: {
src: params.row.logo || '',
alt: "加载图片失败",
},
style: {
cursor: "pointer",
width: "80px",
height: "60px",
margin: "10px 0",
"object-fit": "contain",
},
});
},
},
{
title: "状态",
key: "deleteFlag",
align: "left",
render: (h, params) => {
return h(
"i-switch",
{
props: {
value: params.row.deleteFlag == 0,
size: "large",
},
on: {
"on-change": (checked) => {
this.handleStatusSwitchChange(params.row, checked);
},
},
},
[
h("span", { slot: "open" }, "启用"),
h("span", { slot: "close" }, "禁用"),
]
);
},
filters: [
{
label: "启用",
value: 0,
},
{
label: "禁用",
value: 1,
},
],
filterMultiple: false,
filterMethod(value, row) {
if (value == 0) {
return row.deleteFlag == 0;
} else if (value == 1) {
return row.deleteFlag == 1;
}
},
},
{
title: "操作",
key: "action",
width: 210,
align: "center",
fixed: "right",
render: (h, params) => {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.edit(params.row);
},
},
},
"编辑"
),
h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.openCategoryModal(params.row);
},
},
},
"关联分类"
),
h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.delBrand(params.row.id);
},
},
},
"删除"
),
]);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
submitLoading: false,
data: [],
total: 0,
};
},
methods: {
@@ -287,18 +210,13 @@ export default {
this.picModelFlag = false;
this.form.logo = val.url;
},
buildCategoryTreeNodes(list, selectedSet) {
buildCategoryTreeNodes(list) {
if (!Array.isArray(list) || list.length === 0) return [];
return list.map((item) => {
const children = this.buildCategoryTreeNodes(item.children || [], selectedSet);
return {
return list.map((item) => ({
id: item.id,
title: item.name,
expand: true,
checked: selectedSet.has(item.id),
children,
};
});
children: this.buildCategoryTreeNodes(item.children || []),
}));
},
async openCategoryModal(row) {
this.currentBrandId = row.id;
@@ -319,22 +237,16 @@ export default {
.filter(Boolean)
: [];
this.selectedCategoryIds = selectedIds;
const selectedSet = new Set(selectedIds);
this.categoryTreeData = treeRes && treeRes.success
? this.buildCategoryTreeNodes(treeRes.result || [], selectedSet)
this.categoryTreeData =
treeRes && treeRes.success
? this.buildCategoryTreeNodes(treeRes.result || [])
: [];
} finally {
this.categoryTreeLoading = false;
}
},
onCategoryTreeCheckChange(checkedNodes) {
if (!Array.isArray(checkedNodes)) {
this.selectedCategoryIds = [];
return;
}
this.selectedCategoryIds = checkedNodes
.map((node) => node && node.id)
.filter(Boolean);
onCategoryTreeCheckChange(_data, checkedInfo) {
this.selectedCategoryIds = checkedInfo?.checkedKeys || [];
},
submitBrandCategory() {
if (!this.currentBrandId) return;
@@ -342,14 +254,15 @@ export default {
saveBrandCategory(
this.currentBrandId,
(this.selectedCategoryIds || []).map((id) => String(id))
).then((res) => {
)
.then((res) => {
this.categorySubmitLoading = false;
if (res && res.success) {
this.$Message.success("操作成功");
this.categoryModalVisible = false;
return;
}
}).catch(() => {
})
.catch(() => {
this.categorySubmitLoading = false;
});
},
@@ -373,36 +286,29 @@ export default {
},
});
},
// 删除品牌
async delBrand(id) {
let res = await delBrand(id);
const res = await delBrand(id);
if (res.success) {
this.$Message.success("品牌删除成功!");
this.getDataList();
}
},
// 初始化数据
init() {
this.getDataList();
},
// 分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
// 获取数据
getDataList() {
this.loading = true;
getManagerBrandPage(this.searchForm).then((res) => {
@@ -413,13 +319,11 @@ export default {
}
});
},
// 提交表单
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
if (this.modalType === 0) {
// 添加 避免编辑后传入id等数据 记得删除
delete this.form.id;
addBrand(this.form).then((res) => {
this.submitLoading = false;
@@ -430,7 +334,6 @@ export default {
}
});
} else {
// 编辑
updateBrand(this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
@@ -443,28 +346,24 @@ export default {
}
});
},
// 添加
add() {
this.modalType = 0;
this.modalTitle = "添加";
this.$refs.form.resetFields();
this.$refs.form?.resetFields();
delete this.form.id;
this.modalVisible = true;
},
// 编辑
edit(v) {
this.modalType = 1;
this.modalTitle = "编辑";
this.$refs.form.resetFields();
// 转换null为""
for (let attr in v) {
this.$refs.form?.resetFields();
for (const attr in v) {
if (v[attr] === null) {
v[attr] = "";
}
}
let str = JSON.stringify(v);
let data = JSON.parse(str);
const str = JSON.stringify(v);
const data = JSON.parse(str);
this.form = data;
this.modalVisible = true;
},
@@ -474,3 +373,6 @@ export default {
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@@ -1,148 +1,146 @@
<template>
<div>
<Card>
<el-card>
<div class="mb_10">
<Button @click="addParent" icon="md-add">添加一级分类</Button>
<el-button type="primary" @click="addParent">添加一级分类</el-button>
</div>
<Table
update-show-children
<el-table
v-loading="loading"
class="table"
:load-data="handleLoadData"
row-key="id"
:loading="loading"
:data="tableData"
:columns="columns"
>
<template slot="action" slot-scope="scope">
<div class="ops">
<a class="ops-link" @click="edit(scope.row)">编辑</a>
<a class="ops-link" @click="remove(scope.row)">删除</a>
<a v-show="scope.row.level != 2" class="ops-link" @click="addChildren(scope.row)">添加子分类</a>
</div>
</template>
<template slot="commissionRate" slot-scope="scope">
<priceColorScheme v-if="scope.row.commissionRate > 0" unit="" :color="$mainColor" :value="scope.row.commissionRate">%</priceColorScheme>
<priceColorScheme v-else :value="scope.row.commissionRate" unit="" >%</priceColorScheme>
<!-- {{ scope.row.commissionRate }}% -->
</template>
<template slot="deleteFlag" slot-scope="{ row }">
<i-switch
size="large"
v-model="row.deleteFlag"
:true-value="false"
:false-value="true"
:loading="row._statusLoading"
@on-change="onStatusSwitchChange(row, $event)"
>
<span slot="open">开启</span>
<span slot="close">关闭</span>
</i-switch>
</template>
</Table>
<Modal
:title="modalTitle"
v-model="modalVisible"
:mask-closable="false"
:width="500"
>
<Form ref="form" :model="formAdd" :label-width="100" :rules="formValidate">
<div v-if="showParent">
<FormItem label="上级分类" prop="parentId">
{{ parentTitle }}
<Input
v-model="formAdd.parentId"
clearable
style="width: 100%; display: none"
/>
</FormItem>
</div>
<FormItem label="层级" prop="level" style="display: none">
<Input v-model="formAdd.level" clearable style="width: 100%" />
</FormItem>
<FormItem label="分类名称" prop="name">
<Input v-model="formAdd.name" clearable style="width: 100%" />
</FormItem>
<FormItem label="分类图标" prop="image" v-if="formAdd.level !== 1">
<upload-pic-input
v-model="formAdd.image"
row-key="id"
border
:tree-props="{ children: 'children' }"
style="width: 100%"
></upload-pic-input>
</FormItem>
<FormItem label="排序值" prop="sortOrder" style="width: 345px">
<InputNumber v-model="formAdd.sortOrder"></InputNumber>
</FormItem>
<FormItem label="佣金比例(%)" prop="commissionRate" style="width: 345px">
<InputNumber :max="100" :min="0" v-model="formAdd.commissionRate"></InputNumber>
</FormItem>
<FormItem label="是否启用" prop="deleteFlag">
<i-switch
size="large"
>
<el-table-column prop="name" label="分类名称" min-width="200" />
<el-table-column label="状态" width="120">
<template #default="{ row }">
<el-switch
v-if="row"
v-model="row.deleteFlag"
:active-value="false"
:inactive-value="true"
inline-prompt
active-text="开启"
inactive-text="关闭"
:loading="!!row._statusLoading"
@change="(val) => onStatusSwitchChange(row, val)"
/>
</template>
</el-table-column>
<el-table-column label="佣金" width="120">
<template #default="{ row }">
<span
v-if="row"
:style="row.commissionRate > 0 ? { color: $mainColor } : {}"
>
{{ row.commissionRate }}%
</span>
</template>
</el-table-column>
<el-table-column label="操作" min-width="220" fixed="right">
<template #default="{ row }">
<div v-if="row" class="ops">
<a class="link-text" @click="edit(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
<template v-if="row.level != 2">
<span class="op-split">|</span>
<a class="link-text" @click="addChildren(row)">添加子分类</a>
</template>
</div>
</template>
</el-table-column>
</el-table>
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="form" :model="formAdd" label-width="100px" :rules="formValidate">
<el-form-item v-if="showParent" label="上级分类" prop="parentId">
{{ parentTitle }}
<el-input v-model="formAdd.parentId" style="display: none" />
</el-form-item>
<el-form-item label="层级" prop="level" style="display: none">
<el-input v-model="formAdd.level" />
</el-form-item>
<el-form-item label="分类名称" prop="name">
<el-input v-model="formAdd.name" clearable />
</el-form-item>
<el-form-item v-if="formAdd.level !== 1" label="分类图标" prop="image">
<upload-pic-input v-model="formAdd.image" style="width: 100%" />
</el-form-item>
<el-form-item label="排序值" prop="sortOrder">
<el-input-number v-model="formAdd.sortOrder" style="width: 200px" />
</el-form-item>
<el-form-item label="佣金比例(%)" prop="commissionRate">
<el-input-number v-model="formAdd.commissionRate" :min="0" :max="100" style="width: 200px" />
</el-form-item>
<el-form-item label="是否启用" prop="deleteFlag">
<el-switch
v-model="formAdd.deleteFlag"
:true-value="false"
:false-value="true"
>
<span slot="open">启用</span>
<span slot="close">禁用</span>
</i-switch>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="Submit">提交</Button>
</div>
</Modal>
:active-value="false"
:inactive-value="true"
inline-prompt
active-text="启用"
inactive-text="禁用"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="Submit">提交</el-button>
</template>
</el-dialog>
<Modal
:title="modalBrandTitle"
<el-dialog
v-model="modalBrandVisible"
:mask-closable="false"
:width="500"
:title="modalBrandTitle"
width="500px"
:close-on-click-modal="false"
>
<Form ref="brandForm" :model="brandForm" :label-width="100">
<Select v-model="brandForm.categoryBrands" filterable multiple>
<Option v-for="item in brandWay" :value="item.id" :key="item.id">{{
item.name
}}</Option>
</Select>
</Form>
<div slot="footer">
<Button type="text" @click="modalBrandVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="saveCategoryBrand"
>提交</Button
>
</div>
</Modal>
<el-form ref="brandForm" :model="brandForm" label-width="100px">
<el-select v-model="brandForm.categoryBrands" filterable multiple style="width: 100%">
<el-option v-for="item in brandWay" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form>
<template #footer>
<el-button @click="modalBrandVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="saveCategoryBrand">提交</el-button>
</template>
</el-dialog>
<Modal
:title="modalSpecTitle"
<el-dialog
v-model="modalSpecVisible"
:mask-closable="false"
:width="500"
:title="modalSpecTitle"
width="500px"
:close-on-click-modal="false"
>
<Form ref="specForm" :model="specForm" :label-width="100">
<Select v-model="specForm.categorySpecs" multiple>
<Option
<el-form ref="specForm" :model="specForm" label-width="100px">
<el-select v-model="specForm.categorySpecs" multiple style="width: 100%">
<el-option
v-for="item in specifications"
:value="item.id"
:key="item.id"
:label="item.specName"
>
</Option>
</Select>
</Form>
<div slot="footer">
<Button type="text" @click="modalSpecVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="saveCategorySpec"
>提交</Button
>
</div>
</Modal>
</Card>
:value="item.id"
/>
</el-select>
</el-form>
<template #footer>
<el-button @click="modalSpecVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="saveCategorySpec">提交</el-button>
</template>
</el-dialog>
</el-card>
</div>
</template>
<script>
import {
delCategory,
@@ -157,9 +155,9 @@ saveCategoryBrand,
saveCategorySpec,
updateCategory,
} from "@/api/goods";
import uploadPicInput from "@/components/lili/upload-pic-input";
import { regular } from "@/utils";
export default {
name: "goods-category",
components: {
@@ -167,69 +165,44 @@ export default {
},
data() {
return {
recordLevel:[], // 记录当前层级
submitLoading: false, //加载状态
categoryList: [], // 分类列表
loading: false, // 加载状态
brands: [], //品牌集合
specifications: [], //规格集合
categoryId: "", // 分类id
categorySpecs: [], //已经选择的规格
modalType: 0, // 添加或编辑标识
modalVisible: false, // 添加或编辑显示
modalBrandVisible: false, //品牌关联编辑显示
modalSpecVisible: false, //品牌关联编辑显示
modalTitle: "", // 添加或编辑标题
showParent: false, // 是否展示上级菜单
parentTitle: "", // 父级菜单名称
modalBrandTitle: "", // 品牌弹窗标题
modalSpecTitle: "", // 规格弹窗标题
submitLoading: false,
categoryList: [],
loading: false,
brands: [],
specifications: [],
categoryId: "",
categorySpecs: [],
modalType: 0,
modalVisible: false,
modalBrandVisible: false,
modalSpecVisible: false,
modalTitle: "",
showParent: false,
parentTitle: "",
modalBrandTitle: "",
modalSpecTitle: "",
formAdd: {
// 添加或编辑表单对象初始化数据
parentId: "",
name: "",
image: "",
sortOrder: 0,
deleteFlag: 0,
deleteFlag: false,
commissionRate: 0,
level: 0,
},
brandForm: {
categoryBrands: [],
},
brandWay: "", //请求绑定品牌的信息
specForm: {}, // 规格数据
// 表单验证规则
brandWay: [],
specForm: {
categorySpecs: [],
},
formValidate: {
commissionRate: [regular.REQUIRED, regular.INTEGER],
name: [regular.REQUIRED, regular.VARCHAR20],
sortOrder: [regular.REQUIRED, regular.INTEGER],
},
columns: [
{
title: "分类名称",
key: "name",
tree: true,
},
{
title: "状态",
slot: "deleteFlag",
},
{
title: "佣金",
key: "commissionRate",
slot: "commissionRate",
},
{
title: "操作",
key: "action",
slot: "action",
},
],
tableData: [], // 表格数据
checkedCategoryChildren: "", //选中的分类子级
tableData: [],
};
},
methods: {
@@ -250,48 +223,38 @@ export default {
const isClosing = nextDeleteFlag === true;
this.$Modal.confirm({
title: isClosing ? "确认关闭" : "确认开启",
content:
"您是否要" +
(isClosing ? "关闭" : "开启") +
"当前分类 " +
row.name +
" 及其子分类?",
content: `您是否要${isClosing ? "关闭" : "开启"}当前分类 ${row.name} 及其子分类?`,
loading: true,
okText: "是",
cancelText: "否",
onOk: () => {
this.$set(row, "_statusLoading", true);
disableCategory(row.id, { enableOperations: isClosing ? true : 0 }).then(
(res) => {
row._statusLoading = true;
disableCategory(row.id, { enableOperations: isClosing ? true : 0 }).then((res) => {
this.$Modal.remove();
this.$set(row, "_statusLoading", false);
row._statusLoading = false;
if (res && res.success) {
this.$Message.success("操作成功");
this.getAllList(0);
this.getAllList();
return;
}
row.deleteFlag = previousDeleteFlag;
}
);
});
},
onCancel: () => {
row.deleteFlag = previousDeleteFlag;
},
});
},
// 初始化数据
init() {
this.getAllList(0);
this.getAllList();
this.getBrandList();
this.getSpecList();
},
//获取所有品牌
getBrandList() {
getBrandListData().then((res) => {
this.brandWay = res;
});
},
//获取所有规格
getSpecList() {
getSpecificationList().then((res) => {
if (res.length != 0) {
@@ -299,7 +262,6 @@ export default {
}
});
},
//弹出品牌关联框
brandOperation(v) {
getCategoryBrandListData(v.id).then((res) => {
this.categoryId = v.id;
@@ -308,7 +270,6 @@ export default {
this.modalBrandVisible = true;
});
},
//弹出规格关联框
specOperation(v) {
getCategorySpecListData(v.id).then((res) => {
this.categoryId = v.id;
@@ -317,7 +278,6 @@ export default {
this.modalSpecVisible = true;
});
},
//保存分类规格绑定
saveCategorySpec() {
saveCategorySpec(this.categoryId, this.specForm).then((res) => {
this.submitLoading = false;
@@ -327,7 +287,6 @@ export default {
}
});
},
//保存分类品牌绑定
saveCategoryBrand() {
saveCategoryBrand(this.categoryId, this.brandForm).then((res) => {
this.submitLoading = false;
@@ -337,19 +296,17 @@ export default {
}
});
},
// 添加子分类
addChildren(v) {
this.modalType = 0;
this.modalTitle = "添加子分类";
this.parentTitle = v.name;
this.formAdd.level = eval(v.level + "+1");
this.formAdd.level = Number(v.level) + 1;
this.formAdd.commissionRate = v.commissionRate;
this.showParent = true;
delete this.formAdd.id;
this.formAdd.parentId = v.id;
this.modalVisible = true;
},
// 编辑分类
edit(v) {
this.modalType = 1;
this.modalTitle = "编辑";
@@ -364,24 +321,22 @@ export default {
this.showParent = false;
this.modalVisible = true;
},
// 添加一级分类
addParent() {
this.modalType = 0;
this.modalTitle = "添加一级分类";
this.parentTitle = "顶级分类";
this.showParent = true;
this.$refs.form.resetFields();
this.$refs.form?.resetFields();
delete this.formAdd.id;
this.formAdd.parentId = 0;
this.formAdd.level = 0;
this.modalVisible = true;
},
// 提交
Submit() {
this.$refs.form.validate((valid) => {
if (valid) {
if (!valid) return;
this.submitLoading = true;
if (this.modalType === 0) {
// 添加 避免编辑后传入id等数据 记得删除
delete this.formAdd.id;
insertCategory(this.formAdd).then((res) => {
this.submitLoading = false;
@@ -393,7 +348,6 @@ export default {
}
});
} else {
// 编辑
updateCategory(this.formAdd).then((res) => {
this.submitLoading = false;
if (res.success) {
@@ -404,17 +358,14 @@ export default {
}
});
}
}
});
},
// 删除分类
remove(v) {
this.$Modal.confirm({
title: "确认删除",
content: "您确认要删除 " + v.name + " ?",
loading: true,
onOk: () => {
// 删除
delCategory(v.id).then((res) => {
this.$Modal.remove();
if (res.success) {
@@ -425,80 +376,22 @@ export default {
},
});
},
// 异步手动加载分类名称
handleLoadData(item, callback) {
this.recordLevel[item.level] = item.id;
if (item.level == 0) {
let categoryList = JSON.parse(JSON.stringify(this.categoryList));
categoryList.forEach((val) => {
if (val.id == item.id) {
val.children.map((child) => {
child._loading = false;
child.children = [];
});
this.normalizeCategoryTree(val.children);
// 模拟加载
setTimeout(() => {
callback(val.children);
}, 100);
}
});
} else {
this.deepCategoryChildren(item.id, this.categoryList);
this.normalizeCategoryTree(this.checkedCategoryChildren);
setTimeout(() => {
callback(this.checkedCategoryChildren);
}, 100);
}
},
// 通过递归children来实现手动加载数据
deepCategoryChildren(id, list) {
if (id != "0" && list.length != 0) {
for (let i = 0; i < list.length; i++) {
let item = list[i];
if (item.id == id) {
this.checkedCategoryChildren = item.children;
return;
} else {
this.deepCategoryChildren(id, item.children);
}
}
}
},
// 获取分类数据
getAllList() {
this.loading = true;
getCategoryTree().then((res) => {
getCategoryTree()
.then((res) => {
this.loading = false;
if (res.success) {
localStorage.setItem("category", JSON.stringify(res.result));
this.normalizeCategoryTree(res.result);
this.categoryList = JSON.parse(JSON.stringify(res.result));
this.tableData = res.result.map((item) => {
if(this.recordLevel[0] && item.id === this.recordLevel[0]) {
item._showChildren = true
// 继续判断第二层
if(this.recordLevel[1] && item.children){
item.children.map((child)=>{
if(this.recordLevel[1] && child.id === this.recordLevel[1]){
child._showChildren = true
this.tableData = JSON.parse(JSON.stringify(res.result));
}
})
}
}else{
if (item.children.length !== 0) {
item.children = [];
item._loading = false;
}
}
return item;
});
}
.catch(() => {
this.loading = false;
});
},
// 启用分类
enable(v) {
this.$Modal.confirm({
title: "确认启用",
@@ -511,16 +404,15 @@ export default {
this.$Modal.remove();
if (res.success) {
this.$Message.success("操作成功");
this.getAllList(0);
this.getAllList();
}
});
},
onCancel: () => {
this.getAllList(0);
this.getAllList();
},
});
},
// 禁用分类
disable(v) {
this.$Modal.confirm({
title: "确认禁用",
@@ -533,12 +425,12 @@ export default {
this.$Modal.remove();
if (res.success) {
this.$Message.success("操作成功");
this.getAllList(0);
this.getAllList();
}
});
},
onCancel: () => {
this.getAllList(0);
this.getAllList();
},
});
},
@@ -548,31 +440,15 @@ export default {
},
};
</script>
<style lang="scss" scoped>
::v-deep .ivu-table-wrapper {
:deep(.el-table__body-wrapper) {
overflow: auto;
}
.table {
min-height: 100vh;
height: auto;
min-height: 60vh;
}
.ops-link {
color: #2d8cf0;
cursor: pointer;
text-decoration: none;
}
.ops {
display: flex;
flex-wrap: wrap;
}
.ops-link + .ops-link {
margin-left: 16px;
position: relative;
}
.ops-link + .ops-link::before {
content: "|";
position: absolute;
left: -10px;
color: #dcdee2;
.mb_10 {
margin-bottom: 10px;
}
</style>

View File

@@ -1,73 +1,92 @@
<template>
<div class="search">
<Card>
<Form ref="form" :model="form" :label-width="100" :rules="formValidate">
<FormItem label="参数名称" prop="paramName">
<Input v-model="form.paramName" clearable style="width: 520px" />
</FormItem>
<FormItem label="是否必填" prop="required">
<RadioGroup v-model="form.required">
<Radio :label="0"></Radio>
<Radio :label="1"></Radio>
</RadioGroup>
<span style="margin-left: 10px; color: #999; font-size: 12px"
>商品发布时参数是否必填</span
>
</FormItem>
<FormItem label="是否索引" prop="isIndex">
<RadioGroup v-model="form.isIndex">
<Radio :label="0"></Radio>
<Radio :label="1"></Radio>
</RadioGroup>
<span style="margin-left: 10px; color: #999; font-size: 12px"
>开启索引后用户将可以通过该参数筛选商品索引开关不影响商详页的参数展示</span
>
</FormItem>
<FormItem label="排序" prop="sort">
<InputNumber :min="0" type="number" v-model="form.sort" style="width: 520px" />
</FormItem>
<FormItem label="参数值" prop="options">
<Table :columns="optionColumns" :data="form.options" border size="small" style="width: 520px" />
<el-card>
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
<el-form-item label="参数名称" prop="paramName">
<el-input v-model="form.paramName" clearable style="width: 520px" />
</el-form-item>
<el-form-item label="是否必填" prop="required">
<el-radio-group v-model="form.required">
<el-radio :value="0"></el-radio>
<el-radio :value="1"></el-radio>
</el-radio-group>
<span style="margin-left: 10px; color: #999; font-size: 12px">商品发布时参数是否必填</span>
</el-form-item>
<el-form-item label="是否索引" prop="isIndex">
<el-radio-group v-model="form.isIndex">
<el-radio :value="0"></el-radio>
<el-radio :value="1"></el-radio>
</el-radio-group>
<span style="margin-left: 10px; color: #999; font-size: 12px">
开启索引后用户将可以通过该参数筛选商品索引开关不影响商详页的参数展示
</span>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number :min="0" v-model="form.sort" style="width: 520px" />
</el-form-item>
<el-form-item label="参数值" prop="options">
<el-table :data="form.options" border size="small" style="width: 520px">
<el-table-column label="参数值" min-width="420">
<template #default="{ row, $index }">
<el-input
v-if="row"
v-model="form.options[$index].value"
clearable
@blur="touchOptionsValidate"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="90" align="center">
<template #default="{ row, $index }">
<a v-if="row" class="link-text" @click="removeOptionRow($index)">
删除
</a>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 10px">
<Button type="primary" @click="addOptionRow">新增</Button>
<el-button type="primary" @click="addOptionRow">新增</el-button>
</div>
</FormItem>
<FormItem label="关联分类">
<Button type="default" @click="openCategoryModal">选择分类</Button>
<span v-if="selectedCategoryNamesText" style="margin-left: 10px; color: #999; font-size: 12px"
>{{ selectedCategoryNamesText }}</span
>
<span v-else style="margin-left: 10px; color: #999; font-size: 12px"
>已选择{{ selectedCategoryIds.length }}个分类</span
>
</FormItem>
<FormItem>
<Button type="default" @click="back">返回</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit">保存</Button>
</FormItem>
</Form>
</Card>
</el-form-item>
<el-form-item label="关联分类">
<el-button @click="openCategoryModal">选择分类</el-button>
<span v-if="selectedCategoryNamesText" style="margin-left: 10px; color: #999; font-size: 12px">
{{ selectedCategoryNamesText }}</span>
<span v-else style="margin-left: 10px; color: #999; font-size: 12px">
已选择{{ selectedCategoryIds.length }}个分类
</span>
</el-form-item>
<el-form-item>
<el-button @click="back">返回</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">保存</el-button>
</el-form-item>
</el-form>
</el-card>
<Modal
:title="categoryModalTitle"
<el-dialog
v-model="categoryModalVisible"
:mask-closable="false"
:width="700"
:title="categoryModalTitle"
width="700px"
:close-on-click-modal="false"
>
<div style="position: relative; max-height: 520px; overflow: auto;">
<Spin size="large" fix v-if="categoryTreeLoading"></Spin>
<Tree
<div v-loading="categoryTreeLoading" style="position: relative; max-height: 520px; overflow: auto">
<el-tree
ref="categoryTree"
:key="categoryTreeKey"
:data="categoryTreeData"
:props="{ label: 'title', children: 'children' }"
node-key="id"
show-checkbox
@on-check-change="onCategoryTreeCheckChange"
></Tree>
default-expand-all
:default-checked-keys="selectedCategoryIds"
@check="onCategoryTreeCheckChange"
/>
</div>
<div slot="footer">
<Button type="text" @click="categoryModalVisible = false">取消</Button>
<Button type="primary" style="margin-left: 8px" @click="categoryModalVisible = false">确定</Button>
</div>
</Modal>
<template #footer>
<el-button @click="categoryModalVisible = false">取消</el-button>
<el-button type="primary" style="margin-left: 8px" @click="categoryModalVisible = false">确定</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -190,51 +209,6 @@ export default {
isIndex: 0,
sort: 0,
},
optionColumns: [
{
title: "参数值",
key: "value",
minWidth: 420,
render: (h, params) => {
return h("Input", {
props: {
value: params.row.value,
clearable: true,
},
on: {
input: (val) => {
if (!Array.isArray(this.form.options)) this.form.options = [];
if (!this.form.options[params.index]) return;
this.form.options[params.index].value = val;
},
"on-blur": () => {
this.touchOptionsValidate();
},
},
});
},
},
{
title: "操作",
width: 90,
align: "center",
render: (h, params) => {
return h(
"Button",
{
props: {
type: "error",
size: "small",
},
on: {
click: () => this.removeOptionRow(params.index),
},
},
"删除",
);
},
},
],
formValidate: {
paramName: [regular.REQUIRED, regular.VARCHAR5],
options: [{ required: true, validator: validateOptions, trigger: "change" }],
@@ -253,8 +227,8 @@ export default {
const selectedSet = new Set(toStringArray(this.selectedCategoryIds));
const isSelected = (node) => {
if (!node) return false;
const id = node.id !== undefined && node.id !== null ? String(node.id) : "";
if (id && selectedSet.has(id)) return true;
const nodeId = node.id !== undefined && node.id !== null ? String(node.id) : "";
if (nodeId && selectedSet.has(nodeId)) return true;
const children = Array.isArray(node.children) ? node.children : [];
if (children.length === 0) return false;
return children.every(isSelected);
@@ -276,11 +250,11 @@ export default {
watch: {
"form.required"(val) {
const n = normalize01(val, 0);
if (val !== n) this.$set(this.form, "required", n);
if (val !== n) this.form.required = n;
},
"form.isIndex"(val) {
const n = normalize01(val, 0);
if (val !== n) this.$set(this.form, "isIndex", n);
if (val !== n) this.form.isIndex = n;
},
},
methods: {
@@ -302,18 +276,13 @@ export default {
this.form.options.splice(index, 1);
this.touchOptionsValidate();
},
buildCategoryTreeNodes(list, selectedSet) {
buildCategoryTreeNodes(list) {
if (!Array.isArray(list) || list.length === 0) return [];
return list.map((item) => {
const children = this.buildCategoryTreeNodes(item.children || [], selectedSet);
return {
return list.map((item) => ({
id: item.id,
title: item.name,
expand: true,
checked: selectedSet.has(String(item.id)),
children,
};
});
children: this.buildCategoryTreeNodes(item.children || []),
}));
},
async loadDetail(parameterId) {
if (!parameterId) return;
@@ -337,7 +306,6 @@ export default {
this.categoryTreeLoading = true;
this.categoryTreeKey += 1;
try {
const selectedSet = new Set(toStringArray(this.selectedCategoryIds));
if (!Array.isArray(this.categoryTreeSource) || this.categoryTreeSource.length === 0) {
const treeRes = await getCategoryTree();
this.categoryTreeSource = treeRes && treeRes.success ? treeRes.result || [] : [];
@@ -345,17 +313,13 @@ export default {
buildCategoryIdNameMap(this.categoryTreeSource, map);
this.categoryIdNameMap = map;
}
this.categoryTreeData = this.buildCategoryTreeNodes(this.categoryTreeSource || [], selectedSet);
this.categoryTreeData = this.buildCategoryTreeNodes(this.categoryTreeSource || []);
} finally {
this.categoryTreeLoading = false;
}
},
onCategoryTreeCheckChange(checkedNodes) {
if (!Array.isArray(checkedNodes)) {
this.selectedCategoryIds = [];
return;
}
this.selectedCategoryIds = toStringArray(checkedNodes.map((node) => node && node.id).filter(Boolean));
onCategoryTreeCheckChange(_data, checkedInfo) {
this.selectedCategoryIds = toStringArray(checkedInfo?.checkedKeys || []);
},
initForm() {
if (this.id) {

View File

@@ -1,55 +1,72 @@
<template>
<div class="search">
<Card>
<Form
<el-card>
<el-form
ref="searchForm"
@submit.native.prevent
@keydown.enter.native="handleSearch"
:model="searchForm"
inline
:label-width="70"
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<Form-item label="参数名称">
<Input
type="text"
<el-form-item label="参数名称">
<el-input
v-model="searchForm.paramName"
placeholder="请输入参数名称"
clearable
style="width: 240px"
/>
</Form-item>
<Button @click="handleSearch" type="primary">搜索</Button>
</Form>
</Card>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<Card>
<Row class="operation padding-row">
<Button @click="goAdd" type="primary">添加</Button>
</Row>
<Table :loading="loading" border :columns="columns" :data="data" ref="table"></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
<el-card>
<div class="operation padding-row">
<el-button type="primary" @click="goAdd">添加</el-button>
</div>
<el-table v-loading="loading" border :data="data" ref="table" style="width: 100%">
<el-table-column prop="paramName" label="参数名称" width="300" />
<el-table-column prop="options" label="参数值" min-width="260" show-overflow-tooltip />
<el-table-column label="必填" width="300" align="center">
<template #default="{ row }">
<span v-if="row">{{ isTruthyFlag(row.required) ? "是" : "否" }}</span>
</template>
</el-table-column>
<el-table-column label="可索引" width="300" align="center">
<template #default="{ row }">
<span v-if="row">{{ isTruthyFlag(row.isIndex) ? "是" : "否" }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="300" align="center" fixed="right">
<template #default="{ row }">
<div v-if="row">
<a class="link-text" @click="goEdit(row)">编辑</a>
<span class="op-split">|</span>|<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</div>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</div>
</template>
<script>
import {
deleteParams,
getGoodsParamsPage,
} from "@/api/goods";
import { deleteParams, getGoodsParamsPage } from "@/api/goods";
export default {
name: "categoryParams",
@@ -66,90 +83,12 @@ export default {
paramName: "",
},
data: [],
columns: [
{
title: "参数名称",
key: "paramName",
width: 300,
resizable: true,
sortable: false,
},
{
title: "参数值",
key: "options",
minWidth: 260,
tooltip: true,
},
{
title: "必填",
key: "required",
width: 300,
align: "center",
render: (h, params) => {
const val = params.row.required;
const on = val === 1 || val === "1" || val === true;
return h("span", on ? "是" : "否");
},
},
{
title: "可索引",
key: "isIndex",
width: 300,
align: "center",
render: (h, params) => {
const val = params.row.isIndex;
const on = val === 1 || val === "1" || val === true;
return h("span", on ? "是" : "否");
},
},
{
title: "操作",
key: "action",
width: 300,
align: "center",
fixed: "right",
render: (h, params) => {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.goEdit(params.row);
},
},
},
"编辑"
),
h("span", { style: { margin: "0 8px", color: "#dcdee2" } }, "|"),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.remove(params.row);
},
},
},
"删除"
),
]);
},
},
],
};
},
methods: {
isTruthyFlag(val) {
return val === 1 || val === "1" || val === true;
},
init() {
this.getDataList();
},
@@ -209,4 +148,10 @@ export default {
},
};
</script>
<style lang="scss"></style>
<style lang="scss">
.link-text {
color: #409eff;
cursor: pointer;
text-decoration: none;
}
</style>

View File

@@ -1,50 +1,113 @@
<template>
<div>
<Card>
<Row>
<Form ref="searchForm" :model="searchForm" @keydown.enter.native="handleSearch" @submit.native.prevent inline :label-width="70" class="search-form">
<Form-item label="会员名称" prop="memberName">
<Input type="text" v-model="searchForm.memberName" placeholder="请输入会员名称" clearable style="width: 240px"/>
</Form-item>
<Form-item label="商品名称" prop="goodsName">
<Input type="text" v-model="searchForm.goodsName" placeholder="请输入商品名称" clearable style="width: 240px"/>
</Form-item>
<Button @click="handleSearch" type="primary" class="search-btn" icon="ios-search">搜索</Button>
</Form>
</Row>
</Card>
<Card>
<Table :loading="loading" border :columns="columns" :data="data" ref="table" class="mt_10">
<!-- 页面展示 -->
<template slot="shopDisableSlot" slot-scope="scope">
<i-switch size="large" true-value="OPEN" false-value="CLOSE" v-model="scope.row.status"
@on-change="changeSwitch(scope.row)">
<span slot="open">展示</span>
<span slot="close">隐藏</span>
</i-switch>
</template>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page :current="searchForm.pageNumber" :total="total" :page-size="searchForm.pageSize" @on-change="changePage"
@on-page-size-change="changePageSize" :page-size-opts="[20, 50, 100]"
size="small" show-total show-elevator show-sizer></Page>
</Row>
</Card>
<!-- 评价详情 -->
<Modal v-model="infoFlag" width="800" :title="infoTitle">
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<el-form-item label="会员名称" prop="memberName">
<el-input
v-model="searchForm.memberName"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item label="商品名称" prop="goodsName">
<el-input
v-model="searchForm.goodsName"
placeholder="请输入商品名称"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<el-table ref="table" v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
<el-table-column prop="goodsName" label="商品名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="memberName" label="会员名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="content" label="评论内容" min-width="220" show-overflow-tooltip />
<el-table-column label="是否置顶" width="100" align="center">
<template #default="{ row }">
<el-tag v-if="row" :type="row.top ? 'danger' : 'info'">
{{ row.top ? "已置顶" : "未置顶" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="评价" width="90">
<template #default="{ row }">
<el-tag v-if="row" :type="gradeTagType(row.grade)">{{ gradeText(row.grade) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="评价时间" width="170" />
<el-table-column label="页面展示" width="100">
<template #default="{ row }">
<el-switch
v-if="row"
v-model="row.status"
active-value="OPEN"
inactive-value="CLOSE"
inline-prompt
active-text="展示"
inactive-text="隐藏"
@change="changeSwitch(row)"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="220" align="center" fixed="right">
<template #default="{ row }">
<div v-if="row" class="ops">
<a class="link-text" @click="info(row)">查看</a>
<span class="op-split">|</span>
<a class="link-text" @click="updateTop(row)">{{ row.top ? "取消置顶" : "置顶评论" }}</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</div>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<el-dialog v-model="infoFlag" :title="infoTitle" width="800px">
<div class="info-list" style="overflow: hidden">
<div class="left-container">
<div class="product">
<img class="img" :src=infoData.goodsImage>
<img class="img" :src="infoData.goodsImage" />
</div>
<div class="show">
<label>页面展示</label>
<i-switch size="large" true-value="OPEN" false-value="CLOSE" v-model="infoData.status"
@on-change="changeSwitchView" style="margin-top: 3px">
<span slot="open">展示</span>
<span slot="close">隐藏</span>
</i-switch>
<el-switch
v-model="infoData.status"
active-value="OPEN"
inactive-value="CLOSE"
inline-prompt
active-text="展示"
inactive-text="隐藏"
style="margin-top: 3px"
@change="changeSwitchView"
/>
</div>
</div>
<div class="right-container">
@@ -53,34 +116,35 @@
<div class="div-height">店铺名称{{ infoData.storeName }}</div>
<div class="div-height">订单号{{ infoData.orderNo }}</div>
</div>
<div class="border-b">
<List>
<ListItem>
<ListItemMeta :avatar="infoData.memberProfile" :title="infoData.memberName"
:description="infoData.content"/>
</ListItem>
<div class="review-item">
<el-avatar v-if="infoData.memberProfile" :src="infoData.memberProfile" :size="40" />
<div class="review-meta">
<div class="review-name">{{ infoData.memberName }}</div>
<div class="review-content">{{ infoData.content }}</div>
</div>
</div>
<div class="score-content">
<span>物流评分{{ infoData.deliveryScore }}</span>
<span>服务评分{{ infoData.serviceScore }}</span>
<span>描述评分{{ infoData.descriptionScore }}</span>
</div>
<div class="" v-if="infoData.haveImage">
<div v-if="infoData.haveImage">
评价图
<div style="margin-left: 40px">
<template v-if="infoData.images && infoData.images.length">
<img style="width: 100px;height: 110px;margin-left: 2px"
v-for="(img,index) in infoData.images.split(',')" :src="img"
alt="" :key="index"/>
<img
v-for="(img, index) in infoData.images.split(',')"
:key="index"
style="width: 100px; height: 110px; margin-left: 2px"
:src="img"
alt=""
/>
</template>
</div>
</div>
</List>
</div>
<div class="border-b" v-if="infoData.reply">
<div>
<div v-if="infoData.reply" class="border-b">
<div>
<div style="float: left">商家回复</div>
<div style="margin-left: 60px">{{ infoData.reply }}</div>
@@ -88,16 +152,20 @@
<div v-if="infoData.haveReplyImage">
<div style="margin-left: 60px">
<template v-if="infoData.replyImage && infoData.replyImage.length">
<img style="width: 100px;height: 110px" v-for="(img,index) in infoData.replyImage.split(',')" :key="index"
:src="img" alt=""/>
<img
v-for="(img, index) in infoData.replyImage.split(',')"
:key="index"
style="width: 100px; height: 110px"
:src="img"
alt=""
/>
</template>
</div>
</div>
</div>
</div>
</div>
</div>
</Modal>
</el-dialog>
</div>
</template>
@@ -105,223 +173,66 @@
import * as API_Member from "@/api/member";
export default {
name: "goods-review", // 会员评价
name: "goods-review",
data() {
return {
infoData: {}, // 商品信息
infoFlag: false, // 评价展示
infoTitle: "", // modal名称
loading: true, // 表单加载状态
infoData: {},
infoFlag: false,
infoTitle: "",
loading: true,
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
pageNumber: 1,
pageSize: 20,
goodsName: "",
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
startDate: "", // 起始时间
endDate: "", // 终止时间
sort: "createTime",
order: "desc",
startDate: "",
endDate: "",
},
columns: [
// 表头
{
title: "商品名称",
key: "goodsName",
minWidth: 120,
align: "left",
tooltip: true,
},
{
title: "会员名称",
key: "memberName",
minWidth: 120,
align: "left",
tooltip: true,
},
{
title: "评论内容",
key: "content",
minWidth: 220,
align: "left",
tooltip: true,
},
{
title: "是否置顶",
key: "top",
align: "center",
width: 100,
render: (h, params) => {
return h(
"Tag",
{
props: {
color: params.row.top ? "red" : "default",
},
},
params.row.top ? "已置顶" : "未置顶"
);
},
},
{
title: "评价",
key: "grade",
align: "left",
width: 90,
render: (h, params) => {
if (params.row.grade == "GOOD") {
return h("Tag", {props: {color: "green",},}, "好评");
} else if (params.row.grade == "MODERATE") {
return h("Tag", {props: {color: "orange",},}, "中评");
} else {
return h("Tag", {props: {color: "red",},}, "差评");
}
}
},
{
title: "评价时间",
key: "createTime",
align: "left",
width: 170
},
{
title: "页面展示",
key: "shopDisable",
align: "left",
width: 100,
slot: "shopDisableSlot",
},
{
title: "操作",
key: "action",
width: 220,
align: "center",
fixed: "right",
render: (h, params) => {
return h("div", { class: "ops" }, [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.info(params.row);
},
},
},
"查看"
),
h(
"span",
{
style: {
margin: "0 8px",
color: "#dcdee2",
},
},
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.updateTop(params.row);
},
},
},
params.row.top ? "取消置顶" : "置顶评论"
),
h(
"span",
{
style: {
margin: "0 8px",
color: "#dcdee2",
},
},
"|"
),
h(
"Poptip",
{
props: { confirm: true, title: "确认删除" },
on: {
"on-ok": () => {
this.remove(params.row);
},
},
},
[
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
},
"删除"
),
]
),
]);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
data: [],
total: 0,
};
},
methods: {
// 切换查看switch
gradeTagType(grade) {
if (grade === "GOOD") return "success";
if (grade === "MODERATE") return "warning";
return "danger";
},
gradeText(grade) {
if (grade === "GOOD") return "好评";
if (grade === "MODERATE") return "中评";
return "差评";
},
changeSwitchView(val) {
let status = val;
API_Member.updateMemberReview(this.infoData.id, {status}).then(
(res) => {
const status = val;
API_Member.updateMemberReview(this.infoData.id, { status }).then(() => {
this.$Message.success("修改成功!");
this.init();
}
);
});
},
// 初始化数据
init() {
this.getDataList();
},
// 分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
this.clearSelectAll();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
//列表直接选择页面是否展示
changeSwitch(v) {
let status = v.status;
API_Member.updateMemberReview(v.id, {status: status}).then((res) => {
const status = v.status;
API_Member.updateMemberReview(v.id, { status }).then(() => {
this.init();
});
},
// 置顶评论
updateTop(v) {
const top = !v.top;
API_Member.updateMemberReviewTop(v.id, { top }).then((res) => {
@@ -340,7 +251,6 @@ export default {
}
});
},
// 获取列表
getDataList() {
this.loading = true;
API_Member.getMemberReview(this.searchForm).then((res) => {
@@ -353,7 +263,6 @@ export default {
this.total = this.data.length;
this.loading = false;
},
//评价详情
info(v) {
this.infoFlag = true;
this.infoTitle = `用户${v.memberName}的评价详情`;
@@ -363,14 +272,13 @@ export default {
}
});
},
// 删除评论
remove(v) {
this.$Modal.confirm({
title: "确认删除",
content: "您确认要删除会员" + v.memberName + "的评论?",
loading: true,
onOk: () => {
API_Member.delMemberReview(v.id).then((res) => {
API_Member.delMemberReview(v.id).then(() => {
this.$Modal.remove();
this.$Message.success("修改成功");
this.init();
@@ -384,6 +292,7 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.left-container {
float: left;
@@ -435,7 +344,21 @@ label {
}
.score-content {
margin: 5px 0;
span{margin-right: 20px;}
span {
margin-right: 20px;
}
}
.review-item {
display: flex;
gap: 12px;
margin-bottom: 8px;
}
.review-name {
font-weight: 500;
}
.review-content {
color: #666;
margin-top: 4px;
}
.ops a {
color: #2d8cf0;

View File

@@ -1,61 +1,70 @@
<template>
<div class="search">
<Card>
<Row class="operation padding-row">
<Button type="primary" @click="openAdd">添加分组</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
class="mt_10"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
<el-card>
<div class="operation padding-row">
<el-button type="primary" @click="openAdd">添加分组</el-button>
</div>
<el-table ref="table" v-loading="loading" :data="data" border class="mt_10" style="width: 100%">
<el-table-column prop="groupName" label="分组名称" min-width="160" show-overflow-tooltip />
<el-table-column prop="description" label="分组描述" min-width="240" show-overflow-tooltip />
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column prop="updateTime" label="更新时间" width="180" />
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<div v-if="row" class="ops" style="display: flex; justify-content: center">
<a class="link-text" @click="openEdit(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</div>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
<Modal v-model="addFlag" title="添加商品分组">
<Form ref="addForm" :model="formAdd" :rules="rulesAdd" :label-width="90">
<FormItem label="分组名称" prop="groupName" style="width: 90%;">
<Input v-model="formAdd.groupName" maxlength="30" placeholder="请输入分组名称" />
</FormItem>
<FormItem label="分组描述" prop="description" style="width: 90%;">
<Input v-model="formAdd.description" maxlength="200" placeholder="请输入分组描述" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="addFlag = false">取消</Button>
<Button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</Button>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</Modal>
<Modal v-model="editFlag" title="编辑商品分组">
<Form ref="editForm" :model="formEdit" :rules="rulesEdit" :label-width="90">
<Input v-model="formEdit.id" v-show="false" />
<FormItem label="分组名称" prop="groupName" style="width: 90%;">
<Input v-model="formEdit.groupName" maxlength="30" placeholder="请输入分组名称" />
</FormItem>
<FormItem label="分组描述" prop="description" style="width: 90%;">
<Input v-model="formEdit.description" maxlength="200" placeholder="请输入分组描述" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="editFlag = false">取消</Button>
<Button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</Button>
</div>
</Modal>
</el-card>
<el-dialog v-model="addFlag" title="添加商品分组" width="500px" :close-on-click-modal="false" destroy-on-close>
<el-form ref="addForm" :model="formAdd" :rules="rulesAdd" label-width="90px">
<el-form-item label="分组名称" prop="groupName" style="width: 90%">
<el-input v-model="formAdd.groupName" maxlength="30" placeholder="请输入分组名称" />
</el-form-item>
<el-form-item label="分组描述" prop="description" style="width: 90%">
<el-input v-model="formAdd.description" maxlength="200" placeholder="请输入分组描述" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="addFlag = false">取消</el-button>
<el-button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</el-button>
</template>
</el-dialog>
<el-dialog v-model="editFlag" title="编辑商品分组" width="500px" :close-on-click-modal="false" destroy-on-close>
<el-form ref="editForm" :model="formEdit" :rules="rulesEdit" label-width="90px">
<el-input v-model="formEdit.id" style="display: none" />
<el-form-item label="分组名称" prop="groupName" style="width: 90%">
<el-input v-model="formEdit.groupName" maxlength="30" placeholder="请输入分组名称" />
</el-form-item>
<el-form-item label="分组描述" prop="description" style="width: 90%">
<el-input v-model="formEdit.description" maxlength="200" placeholder="请输入分组描述" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="editFlag = false">取消</el-button>
<el-button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -71,54 +80,6 @@ export default {
pageNumber: 1,
pageSize: 20,
},
columns: [
{
title: "分组名称",
key: "groupName",
minWidth: 160,
tooltip: true,
},
{
title: "分组描述",
key: "description",
minWidth: 240,
tooltip: true,
},
{
title: "创建时间",
key: "createTime",
width: 180,
},
{
title: "更新时间",
key: "updateTime",
width: 180,
},
{
title: "操作",
key: "action",
align: "center",
width: 200,
fixed: "right",
render: (h, params) => {
const linkStyle = {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
};
const sep = h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
);
return h("div", { class: "ops", style: { display: "flex", justifyContent: "center" } }, [
h("a", { style: linkStyle, on: { click: () => this.openEdit(params.row) } }, "编辑"),
sep,
h("a", { style: linkStyle, on: { click: () => this.remove(params.row) } }, "删除"),
]);
},
},
],
data: [],
total: 0,
addFlag: false,
@@ -135,10 +96,10 @@ export default {
description: "",
},
rulesAdd: {
groupName: [{ required: true, message: "请输入分组名称" }],
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
},
rulesEdit: {
groupName: [{ required: true, message: "请输入分组名称" }],
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
},
};
},
@@ -248,7 +209,7 @@ export default {
remove(row) {
this.$Modal.confirm({
title: "提示",
content: "<p>确定删除该分组?</p>",
content: "确定删除该分组?",
onOk: () => {
API_Goods.deleteGoodsGroup(row.id)
.then((res) => {
@@ -271,3 +232,16 @@ export default {
},
};
</script>
<style scoped>
.ops a {
color: #2d8cf0;
cursor: pointer;
text-decoration: none;
}
.ops span {
display: inline-block;
margin: 0 8px;
color: #dcdee2;
}
</style>

View File

@@ -1,120 +1,160 @@
<template>
<div class="search">
<Card>
<Form
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
:label-width="70"
label-width="70px"
class="search-form"
@keydown.enter.native="handleSearch"
@keyup.enter="handleSearch"
>
<Form-item label="商品名称" prop="goodsName">
<Input
<el-form-item label="商品名称" prop="goodsName">
<el-input
v-model="searchForm.goodsName"
placeholder="请输入商品名称"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="商品ID" prop="goodsId">
<Input
</el-form-item>
<el-form-item label="商品ID" prop="goodsId">
<el-input
v-model="searchForm.goodsId"
placeholder="请输入商品ID"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="SKU货号" prop="sn">
<Input
</el-form-item>
<el-form-item label="SKU货号" prop="sn">
<el-input
v-model="searchForm.sn"
placeholder="请输入SKU货号"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="店铺名称" prop="storeName">
<Input
</el-form-item>
<el-form-item label="店铺名称" prop="storeName">
<el-input
v-model="searchForm.storeName"
placeholder="请输入店铺名称"
clearable
style="width: 240px"
/>
</Form-item>
<Button
@click="handleSearch"
class="search-btn"
type="primary"
icon="ios-search"
>
搜索
</Button>
<Button @click="handleReset" style="margin-left: 8px">重置</Button>
</Form>
</Card>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
<el-button style="margin-left: 8px" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<Card class="mt_10">
<Alert show-icon>
支持按规格逐条设置虚拟销量列表总销量 = 真实销量 + 虚拟销量
</Alert>
<el-card class="mt_10">
<el-alert
type="info"
show-icon
:closable="false"
title="支持按规格逐条设置虚拟销量,列表总销量 = 真实销量 + 虚拟销量。"
style="margin-bottom: 10px"
/>
<div class="batch-operations">
<Button
<el-button
type="primary"
:disabled="selectedRows.length === 0"
@click="openBatchVirtualSalesModal"
>
批量设置虚拟销量
</Button>
</el-button>
</div>
<Table
:loading="loading"
:columns="columns"
:data="data"
<el-table
ref="table"
v-loading="loading"
:data="data"
class="mt_10"
sortable="custom"
@on-sort-change="changeSort"
@on-selection-change="handleSelectionChange"
style="width: 100%"
@sort-change="changeSort"
@selection-change="handleSelectionChange"
>
<template slot="goodsSlot" slot-scope="{ row }">
<div class="goods-info">
<el-table-column type="selection" width="55" align="center" />
<el-table-column prop="id" label="SKU ID" width="180" show-overflow-tooltip />
<el-table-column prop="goodsId" label="商品ID" width="180" show-overflow-tooltip />
<el-table-column label="商品信息" min-width="360">
<template #default="{ row }">
<div v-if="row" class="goods-info">
<img
v-if="row.thumbnail"
:src="row.thumbnail"
class="goods-thumbnail"
alt=""
/>
<div class="goods-text">
<div class="div-zoom">{{ row.goodsName }}</div>
<div class="sub-title" v-if="row.simpleSpecs">
<div v-if="row.simpleSpecs" class="sub-title">
规格{{ row.simpleSpecs.trim() || "-" }}
</div>
</div>
</div>
</template>
</Table>
</el-table-column>
<el-table-column label="真实销量" width="110">
<template #default="{ row }">
<span v-if="row">{{ row.buyCount || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="虚拟销量" width="110">
<template #default="{ row }">
<span v-if="row">{{ row.virtualSalesInput || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="总销量" width="110">
<template #default="{ row }">
<span v-if="row">{{ getTotalSales(row) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" width="100">
<template #default="{ row }">
<el-tag v-if="row" :type="row.marketEnable === 'UPPER' ? 'success' : 'warning'">
{{ row.marketEnable === "UPPER" ? "上架" : "下架" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="{ row, $index }">
<a
v-if="row"
class="link-text"
:class="{ disabled: row.saving }"
@click="!row.saving && openVirtualSalesModal(row, $index)"
>
设置
</a>
</template>
</el-table-column>
</el-table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
@on-change="changePage"
@on-page-size-change="changePageSize"
></Page>
</Row>
</Card>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<Modal
<el-dialog
v-model="editModalVisible"
:title="modalMode === 'batch' ? '批量设置虚拟销量' : '设置虚拟销量'"
:mask-closable="false"
:closable="!modalSubmitting"
width="480px"
:close-on-click-modal="false"
:close-on-press-escape="!modalSubmitting"
:show-close="!modalSubmitting"
destroy-on-close
>
<div v-if="modalMode === 'single' && currentSku" class="virtual-sales-modal">
<div class="sku-info-item">
@@ -129,50 +169,42 @@
<span class="sku-info-label">SKU ID</span>
<span>{{ currentSku.id || "-" }}</span>
</div>
<Form :label-width="90" class="mt_10">
<Form-item label="虚拟销量">
<InputNumber
<el-form label-width="90px" class="mt_10">
<el-form-item label="虚拟销量">
<el-input-number
v-model="editForm.virtualSales"
:min="0"
:max="99999999"
:precision="0"
style="width: 200px"
/>
</Form-item>
</Form>
</el-form-item>
</el-form>
</div>
<div v-else-if="modalMode === 'batch'" class="virtual-sales-modal">
<div class="sku-info-item">
<span class="sku-info-label">已选规格</span>
<span>{{ selectedRows.length }} </span>
</div>
<Form :label-width="90" class="mt_10">
<Form-item label="虚拟销量">
<InputNumber
<el-form label-width="90px" class="mt_10">
<el-form-item label="虚拟销量">
<el-input-number
v-model="editForm.virtualSales"
:min="0"
:max="99999999"
:precision="0"
style="width: 200px"
/>
</Form-item>
</Form>
</el-form-item>
</el-form>
</div>
<div slot="footer">
<Button @click="handleCancelVirtualSales" :disabled="modalSubmitting">
取消
</Button>
<Button
type="primary"
:loading="modalSubmitting"
@click="handleSubmitVirtualSales"
>
<template #footer>
<el-button :disabled="modalSubmitting" @click="handleCancelVirtualSales">取消</el-button>
<el-button type="primary" :loading="modalSubmitting" @click="handleSubmitVirtualSales">
提交
</Button>
</div>
</Modal>
</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -216,84 +248,6 @@ export default {
sn: "",
storeName: "",
},
columns: [
{
type: "selection",
width: 60,
align: "center",
},
{
title: "SKU ID",
key: "id",
width: 180,
tooltip: true,
},
{
title: "商品ID",
key: "goodsId",
width: 180,
tooltip: true,
},
{
title: "商品信息",
key: "goodsName",
minWidth: 360,
slot: "goodsSlot",
},
{
title: "真实销量",
key: "buyCount",
width: 110,
render: (h, params) => h("span", params.row.buyCount || 0),
},
{
title: "虚拟销量",
key: "virtualSalesInput",
width: 110,
render: (h, params) => h("span", params.row.virtualSalesInput || 0),
},
{
title: "总销量",
key: "totalSalesDisplay",
width: 110,
render: (h, params) => h("span", this.getTotalSales(params.row)),
},
{
title: "状态",
key: "marketEnable",
width: 100,
render: (h, params) => {
const isUpper = params.row.marketEnable === "UPPER";
return h(
"Tag",
{ props: { color: isUpper ? "green" : "volcano" } },
isUpper ? "上架" : "下架"
);
},
},
{
title: "操作",
key: "action",
width: 100,
align: "center",
render: (h, params) => {
return h(
"Button",
{
props: {
type: "primary",
size: "small",
loading: !!params.row.saving,
},
on: {
click: () => this.openVirtualSalesModal(params.row, params.index),
},
},
"设置"
);
},
},
],
data: [],
total: 0,
};
@@ -328,12 +282,13 @@ export default {
};
this.getDataList();
},
changeSort(e) {
this.searchForm.sort = e.key;
this.searchForm.order = e.order;
if (e.order === "normal") {
changeSort({ prop, order }) {
if (!order) {
this.searchForm.sort = "create_time";
this.searchForm.order = "desc";
} else {
this.searchForm.sort = prop;
this.searchForm.order = order === "ascending" ? "asc" : "desc";
}
this.getDataList();
},
@@ -394,9 +349,7 @@ export default {
this.editModalVisible = true;
},
handleCancelVirtualSales() {
if (this.modalSubmitting) {
return;
}
if (this.modalSubmitting) return;
this.resetVirtualSalesModal();
},
resetVirtualSalesModal() {
@@ -416,18 +369,21 @@ export default {
this.handleBatchSubmitVirtualSales(virtualSales);
return;
}
if (!this.currentSku || this.currentIndex < 0) {
return;
}
if (!this.currentSku || this.currentIndex < 0) return;
const currentIndex = this.currentIndex;
const currentSkuId = this.currentSku.id;
this.modalSubmitting = true;
this.$set(this.data[currentIndex], "saving", true);
if (this.data[currentIndex]) {
this.data[currentIndex].saving = true;
}
updateGoodsSkuVirtualSales(currentSkuId, { virtualSales })
.then((res) => {
if (res && res.success) {
this.$set(this.data[currentIndex], "virtualSales", virtualSales);
this.$set(this.data[currentIndex], "virtualSalesInput", virtualSales);
if (this.data[currentIndex]) {
this.data[currentIndex].virtualSales = virtualSales;
this.data[currentIndex].virtualSalesInput = virtualSales;
}
this.$Message.success("虚拟销量设置成功");
this.resetVirtualSalesModal();
} else {
@@ -439,7 +395,7 @@ export default {
})
.finally(() => {
if (this.data[currentIndex]) {
this.$set(this.data[currentIndex], "saving", false);
this.data[currentIndex].saving = false;
}
this.modalSubmitting = false;
});
@@ -455,9 +411,7 @@ export default {
.then((res) => {
if (res && res.success) {
this.data = this.data.map((item) => {
if (!skuIds.includes(item.id)) {
return item;
}
if (!skuIds.includes(item.id)) return item;
return {
...item,
virtualSales,
@@ -465,9 +419,7 @@ export default {
};
});
this.selectedRows = [];
if (this.$refs.table) {
this.$refs.table.selectAll(false);
}
this.$refs.table?.clearSelection();
this.$Message.success("虚拟销量设置成功");
this.resetVirtualSalesModal();
} else {
@@ -517,6 +469,12 @@ export default {
min-width: 0;
}
.div-zoom {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.batch-operations {
margin-top: 10px;
}
@@ -537,4 +495,8 @@ export default {
color: #808695;
flex-shrink: 0;
}
.mt_10 {
margin-top: 10px;
}
</style>

View File

@@ -10,7 +10,7 @@ h4 {
margin: 20px 0;
font-size: 18px;
}
::v-deep .ivu-icon {
:deep(.ivu-icon){
margin-left: 40px;
margin-right: 40px;
}

View File

@@ -6,7 +6,7 @@
<div class="count-list flex">
<div class="count-item" @click="navigateTo('managerGoods')">
<div>
<Icon class="icon" size="31" type="md-photos" />
<el-icon class="icon" :size="31"><Picture /></el-icon>
</div>
<div>
<div class="counts">{{ homeData.goodsNum || 0 }}</div>
@@ -15,7 +15,7 @@
</div>
<div class="count-item" @click="navigateTo('memberList')">
<div>
<Icon class="icon" size="31" type="md-person" />
<el-icon class="icon" :size="31"><User /></el-icon>
</div>
<div>
<div class="counts">{{ homeData.memberNum || 0 }}</div>
@@ -24,7 +24,7 @@
</div>
<div class="count-item" @click="navigateTo('orderList')">
<div>
<Icon class="icon" size="31" type="md-list" />
<el-icon class="icon" :size="31"><List /></el-icon>
</div>
<div>
<div class="counts">{{ homeData.orderNum || 0 }}</div>
@@ -33,7 +33,7 @@
</div>
<div class="count-item" @click="navigateTo('shopList')">
<div>
<Icon class="icon" size="31" type="ios-stats" />
<el-icon class="icon" :size="31"><DataLine /></el-icon>
</div>
<div>
<div class="counts">{{ homeData.storeNum || 0 }}</div>
@@ -83,8 +83,7 @@
<div class="flow-member">
<div>当前在线人数</div>
<span>
{{ homeData.currentNumberPeopleOnline || 0 }}
</span>
{{ homeData.currentNumberPeopleOnline || 0 }}</span>
</div>
<div class="flow-wrapper">
<h4>流量概括</h4>
@@ -129,7 +128,7 @@
<div class="today-item">
<div>今日交易额</div>
<span v-if="homeData.todayOrderPrice"
>{{ homeData.todayOrderPrice | unitPrice }}</span
>{{ $filters.unitPrice(homeData.todayOrderPrice, '¥') }}</span
>
<span v-else>0.00</span>
</div>
@@ -176,97 +175,47 @@
<!-- top10商品 -->
<div class="card transform">
<h4>热卖商品TOP10</h4>
<Table
stripe
:columns="tophotGoodsColumns"
:data="topHotGoodsData"
></Table>
<el-table stripe :data="topHotGoodsData" style="width: 100%">
<el-table-column type="index" label="排名" width="100" align="center" />
<el-table-column prop="goodsName" label="商品名称" />
<el-table-column prop="price" label="价格">
<template #default="{ row }">
<span :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, '¥') }}</span>
</template>
</el-table-column>
<el-table-column prop="num" label="销量" width="100" sortable />
</el-table>
</div>
<!-- top10店铺 -->
<div class="card transform">
<h4>热卖店铺TOP10</h4>
<Table
stripe
:columns="tophotShopsColumns"
:data="topHotShopsData"
></Table>
<el-table stripe :data="topHotShopsData" style="width: 100%">
<el-table-column type="index" label="排名" width="100" align="center" />
<el-table-column prop="storeName" label="店铺名称" />
<el-table-column prop="price" label="价格">
<template #default="{ row }">
<span :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, '¥') }}</span>
</template>
</el-table-column>
<el-table-column prop="num" label="销量" width="100" sortable />
</el-table>
</div>
</div>
</template>
<script>
import { Picture, User, List, DataLine } from "@element-plus/icons-vue";
import { homeStatistics, hotGoods, hotShops, getNoticePage } from "@/api/index";
import * as API_Goods from "@/api/goods";
import { Chart } from "@antv/g2";
import * as API_Member from "@/api/member";
export default {
name: "home",
components: { Picture, User, List, DataLine },
data() {
return {
// 测试数据
test: {
a: "test",
languages: [],
},
// 测试数据结束
tophotShopsColumns: [
// 表格表头
{
type: "index",
width: 100,
title: "排名",
align: "center",
},
{
title: "店铺名称",
key: "storeName",
},
{
title: "价格",
key: "price",
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.price,color:this.$mainColor}} );
},
},
{
title: "销量",
key: "num",
width: 100,
sortable: true,
},
],
tophotGoodsColumns: [
{
type: "index",
width: 100,
title: "排名",
align: "center",
},
{
title: "商品名称",
key: "goodsName",
},
{
title: "价格",
key: "price",
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.price,color:this.$mainColor}} );
},
},
{
title: "销量",
key: "num",
width: 100,
sortable: true,
},
],
topHotGoodsData: [], //热卖商品集合
topHotShopsData: [], //热卖店铺集合
awaitTodoData: "", //今日待办集合

View File

@@ -1,135 +1,126 @@
<template>
<div class="login" @click="$refs.verify.show = false">
<Row @keydown.enter.native="submitLogin" class="flex">
<Col style="width: 368px">
<el-row class="flex" @keyup.enter="submitLogin">
<el-col style="width: 368px">
<Header />
<Row style="flex-direction: column">
<Form
<el-row style="flex-direction: column">
<el-form
ref="usernameLoginForm"
:model="form"
:rules="rules"
class="form"
>
<FormItem prop="username">
<Input
<el-form-item prop="username">
<el-input
v-model="form.username"
prefix="ios-contact"
size="large"
clearable
placeholder="请输入用户名"
autocomplete="off"
/>
</FormItem>
<FormItem prop="password">
<Input
type="password"
>
<template #prefix>
<el-icon><User /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="form.password"
prefix="ios-lock"
type="password"
size="large"
password
show-password
placeholder="请输入密码"
autocomplete="off"
/>
</FormItem>
</Form>
>
<template #prefix>
<el-icon><Lock /></el-icon>
</template>
</el-input>
</el-form-item>
</el-form>
<Row>
<Button
<el-row>
<el-button
class="login-btn"
type="primary"
size="large"
:loading="loading"
style="width: 100%"
@click="submitLogin"
long
>
<span v-if="!loading">{{ $t("login") }}</span>
<span v-else>{{ $t("logining") }}</span>
</Button>
</Row>
</Row>
<!-- 拼图验证码 -->
</el-button>
</el-row>
</el-row>
<verify
ref="verify"
class="verify-con"
verifyType="LOGIN"
@change="verifyChange"
></verify>
/>
<Footer />
</Col>
<!-- <LangSwitch /> -->
</Row>
</el-col>
</el-row>
</div>
</template>
<script>
import { User, Lock } from "@element-plus/icons-vue";
import { login, userInfo } from "@/api/index";
import Cookies from "js-cookie";
import Header from "@/views/main-parts/header";
import Footer from "@/views/main-parts/footer";
import LangSwitch from "@/views/main-parts/lang-switch";
import util from "@/libs/util.js";
import verify from "@/components/verify";
export default {
components: {
LangSwitch,
Header,
Footer,
verify,
User,
Lock,
},
data() {
return {
loading: false, // 加载状态
loading: false,
form: {
// 表单数据
username: "",
password: "",
mobile: "",
code: "",
},
rules: {
// 验证规则
username: [
{
required: true,
message: "账号不能为空",
trigger: "blur",
},
{ required: true, message: "账号不能为空", trigger: "blur" },
],
password: [
{
required: true,
message: "密码不能为空",
trigger: "blur",
},
{ required: true, message: "密码不能为空", trigger: "blur" },
],
},
};
},
methods: {
afterLogin(res) {
// 登录成功后处理
let accessToken = res.result.accessToken;
let refreshToken = res.result.refreshToken;
const accessToken = res.result.accessToken;
const refreshToken = res.result.refreshToken;
this.setStore("accessToken", accessToken);
this.setStore("refreshToken", refreshToken);
// 获取用户信息
userInfo().then((res) => {
if (res.success) {
// 加载菜单
Cookies.set("userInfoManager", JSON.stringify(res.result));
this.$store.commit("setAvatarPath", res.result.avatar);
util.initRouter(this);
this.$router.push({
name: "home_index",
});
this.$store.commit("setOpenedList");
this.$store.commit("initCachePage");
this.$router.push({ name: "home_index" });
} else {
this.loading = false;
}
});
},
submitLogin() {
// 登录操作
this.$refs.usernameLoginForm.validate((valid) => {
if (valid) {
this.$refs.verify.init();
@@ -137,12 +128,9 @@ export default {
});
},
verifyChange(con) {
// 拼图验证码回显
if (!con.status) return;
this.loading = true;
let fd = new FormData();
const fd = new FormData();
fd.append("username", this.form.username);
fd.append("password", this.md5(this.form.password));
login(fd)
@@ -185,20 +173,10 @@ export default {
.login-btn {
background: linear-gradient(135deg, $theme_color 0%, $warning_color 100%);
height: 40px;
cursor: pointer;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
color: #fff;
width: 100%;
text-align: center;
transition: 0.35s;
border: none;
}
.login-btn:hover {
opacity: 0.9;
border-radius: 10px;
}
}
.flex {

View File

@@ -1,75 +1,97 @@
<template>
<div class="search">
<Card>
<Row class="operation padding-row">
<Button @click="add" type="primary">添加</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
>
<!-- 页面展示 -->
<template slot="disableSlot" slot-scope="{row}">
<i-switch size="large" :true-value="true" :false-value="false" :value="row.switch" @on-change="changeSwitch(row)">
<span slot="open">开启</span>
<span slot="close">禁用</span>
</i-switch>
</template>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
<Modal
:title="modalTitle"
v-model="modalVisible"
:mask-closable="false"
:width="500"
>
<Form ref="form" :model="form" :label-width="120" :rules="formValidate">
<FormItem label="物流公司名称" prop="name">
<Input v-model="form.name" clearable style="width: 100%"/>
</FormItem>
<FormItem label="物流公司代码" prop="code">
<Input v-model="form.code" clearable style="width: 100%"/>
</FormItem>
<FormItem label="支持电子面单">
<i-switch v-model="form.standBy" size="large">
<span slot="open"></span>
<span slot="close"></span>
</i-switch>
</FormItem>
<FormItem label="禁用状态" prop="disabled">
<i-switch true-value="OPEN" false-value="CLOSE" v-model="form.disabled" size="large">
<span slot="open">开启</span>
<span slot="close">禁用</span>
</i-switch>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit"
>提交
</Button
>
<el-card>
<div class="operation padding-row" style="margin-bottom: 10px">
<el-button type="primary" @click="add">添加</el-button>
</div>
</Modal>
<el-table
ref="table"
v-loading="loading"
border
:data="data"
style="width: 100%"
>
<el-table-column prop="name" label="物流公司名称" min-width="120" />
<el-table-column prop="code" label="物流公司编码" min-width="120" />
<el-table-column label="状态" width="150" align="center">
<template #default="{ row }">
<el-switch
v-if="row"
v-model="row.switch"
inline-prompt
active-text="开启"
inactive-text="禁用"
@change="changeSwitch(row)"
/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column label="操作" width="150" align="center">
<template #default="{ row }">
<template v-if="row">
<a class="link-text" @click="detail(row)">修改</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="form" :model="form" :rules="formValidate" label-width="120px">
<el-form-item label="物流公司名称" prop="name">
<el-input v-model="form.name" clearable style="width: 100%" />
</el-form-item>
<el-form-item label="物流公司代码" prop="code">
<el-input v-model="form.code" clearable style="width: 100%" />
</el-form-item>
<el-form-item label="支持电子面单">
<el-switch
v-model="form.standBy"
inline-prompt
active-text=""
inactive-text=""
/>
</el-form-item>
<el-form-item label="禁用状态" prop="disabled">
<el-switch
v-model="form.disabled"
inline-prompt
active-value="OPEN"
inactive-value="CLOSE"
active-text="开启"
inactive-text="禁用"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">
提交
</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -81,27 +103,28 @@
delLogistics,
} from "@/api/logistics";
const defaultForm = () => ({
name: "",
code: "",
standBy: false,
disabled: "CLOSE",
});
export default {
name: "logistics",
data() {
return {
loading: true, // 表单加载状态
modalVisible: false, // 添加或编辑显示
modalTitle: "", // 添加或编辑标题
loading: true,
modalVisible: false,
modalTitle: "",
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
name: "",
},
form: {
// 添加或编辑表单对象初始化数据
name: "",
disabled:"CLOSE"
},
// 表单验证规则
form: defaultForm(),
formValidate: {
name: [
{
@@ -111,144 +134,61 @@
},
],
},
submitLoading: false, // 添加或编辑提交状态
columns: [
{
title: "物流公司名称",
key: "name",
minWidth: 120,
sortable: false,
},
{
title: "物流公司编码",
key: "code",
minWidth: 120,
sortable: false,
},
{
title: "状态",
key: "disabled",
width: 150,
slot: "disableSlot",
},
{
title: "创建时间",
key: "createTime",
width: 180,
sortable: false,
},
{
title: "操作",
key: "action",
align: "center",
width: 150,
render: (h, params) => {
return h("div", [
h(
"Button",
{
props: {
type: "info",
size: "small",
},
style: {
marginRight: "5px",
},
on: {
click: () => {
this.detail(params.row);
},
},
},
"修改"
),
h(
"Button",
{
props: {
type: "error",
size: "small",
},
style: {
marginRight: "5px",
},
on: {
click: () => {
this.remove(params.row);
},
},
},
"删除"
),
]);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
submitLoading: false,
data: [],
total: 0,
id: "",
};
},
methods: {
// 初始化
init() {
this.getDataList();
},
// 分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
changePage() {
this.getDataList();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
changePageSize() {
this.searchForm.pageNumber = 1;
this.getDataList();
},
// 获取列表
getDataList() {
this.loading = true;
getLogisticsPage(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
const data = res.result.records;
data.forEach(e => {
e.switch = e.disabled === 'OPEN' ? true : false;
e.standBy = e.standBy == 'null' || !e.standBy ? false : true;
data.forEach((e) => {
e.switch = e.disabled === "OPEN";
e.standBy = e.standBy == "null" || !e.standBy ? false : !!e.standBy;
});
this.data = data;
console.log(data)
this.total = res.result.total;
}
});
this.total = this.data.length;
this.loading = false;
},
// switch 切换状态
changeSwitch (v) {
this.form.name = v.name;
this.form.code = v.code;
this.form.standBy = v.standBy;
this.form.disabled = v.disabled === 'CLOSE' ? 'OPEN' : 'CLOSE';
updateLogistics(v.id, this.form).then((res) => {
changeSwitch(row) {
this.form.name = row.name;
this.form.code = row.code;
this.form.standBy = row.standBy;
this.form.disabled = row.disabled === "CLOSE" ? "OPEN" : "CLOSE";
updateLogistics(row.id, this.form).then((res) => {
if (res.success) {
this.$Message.success("操作成功");
this.getDataList();
} else {
row.switch = !row.switch;
}
});
},
// 确认提交
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
if (!valid) return;
this.submitLoading = true;
if (this.modalTitle == "添加") {
// 添加 避免编辑后传入id等数据 记得删除
delete this.form.id;
addLogistics(this.form).then((res) => {
if (this.modalTitle === "添加") {
const payload = { ...this.form };
delete payload.id;
addLogistics(payload).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("操作成功");
@@ -257,7 +197,6 @@
}
});
} else {
// 编辑
updateLogistics(this.id, this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
@@ -267,44 +206,33 @@
}
});
}
}
});
},
// 添加信息
add() {
this.modalTitle = "添加";
this.form = {};
this.$refs.form.resetFields();
this.form = defaultForm();
this.$nextTick(() => {
this.$refs.form?.resetFields();
});
this.modalVisible = true;
},
// 编辑
detail(v) {
this.id = v.id;
this.modalTitle = "修改";
this.form = {
name: v.name,
code: v.code,
standBy: v.standBy == "null" || !v.standBy ? false : !!v.standBy,
disabled: v.disabled,
};
this.modalVisible = true;
this.form.name = v.name;
this.form.code = v.code;
console.log(v)
this.form.standBy = v.standBy;
this.form.disabled = v.disabled
},
// 删除物流公司
remove(v) {
this.$Modal.confirm({
title: "确认删除",
// 记得确认修改此处
content: "您确认要删除 " + v.name + " ?",
loading: true,
onOk: () => {
// 删除
delLogistics(v.id).then((res) => {
this.$Modal.remove();
return delLogistics(v.id).then((res) => {
if (res.success) {
this.$Message.success("操作成功");
this.getDataList();

View File

@@ -1,31 +1,24 @@
<template>
<div class="foot">
<Row type="flex" justify="space-around" class="help">
<el-row justify="space-around" class="help">
<a class="item" :href="config.website" target="_blank">帮助</a>
<a class="item" :href="config.website" target="_blank">隐私</a>
<a class="item" :href="config.website" target="_blank">条款</a>
</Row>
<Row type="flex" justify="center" class="copyright">
</el-row>
<el-row justify="center" class="copyright">
Copyright © {{ year }} - Present
<a
:href="config.website"
class="href"
target="_blank"
style="margin: 0 5px"
>{{ config.title }}</a
>
</Row>
<a :href="config.website" class="href" target="_blank" style="margin: 0 5px">{{ config.title }}</a>
</el-row>
</div>
</template>
<script>
const config = require("@/config/index");
export default {
// name: "footer",
data() {
return {
config,
year: new Date().getFullYear(), // 年
year: new Date().getFullYear(),
};
},
};

View File

@@ -1,13 +1,14 @@
<template>
<div>
<Row class="header">
<el-row class="header">
<img :src="domainLogo" class="logo" width="220px" />
</Row>
</el-row>
</div>
</template>
<script>
import { getBaseSite } from "@/api/common.js";
export default {
data() {
return {
@@ -22,44 +23,35 @@ export default {
!localStorage.getItem("icontitle_expiration_time")
) {
this.getSite();
} else {
// 如果缓存过期,则获取最新的信息
if (new Date() > localStorage.getItem("icontitle_expiration_time")) {
} else if (new Date() > localStorage.getItem("icontitle_expiration_time")) {
this.getSite();
return;
} else {
this.domainLogo = localStorage.getItem("icon");
this.applyFavicon(localStorage.getItem("icon"));
window.document.title = localStorage.getItem("title") + " - 运营后台";
}
},
applyFavicon(href) {
let link =
document.querySelector("link[rel*='icon']") ||
document.createElement("link");
link.type = "image/x-icon";
link.href = localStorage.getItem("icon");
link.href = href;
link.rel = "shortcut icon";
document.getElementsByTagName("head")[0].appendChild(link);
window.document.title = localStorage.getItem("title") + " - 运营后台";
}
}
},
getSite() {
//获取domainLogo
getBaseSite().then((res) => {
const { domainLogo, domainIcon, siteName } = JSON.parse(res.result.settingValue);
const { domainLogo, domainIcon, siteName } = JSON.parse(
res.result.settingValue
);
this.domainLogo = domainLogo;
// 过期时间
var expirationTime = new Date().setHours(new Date().getHours() + 1);
// 存放过期时间
const expirationTime = new Date().setHours(new Date().getHours() + 1);
localStorage.setItem("icontitle_expiration_time", expirationTime);
// 存放信息
localStorage.setItem("icon", domainLogo);
localStorage.setItem("domainIcon", domainIcon);
localStorage.setItem("title", siteName);
let link =
document.querySelector("link[rel*='icon']") ||
document.createElement("link");
link.type = "image/x-icon";
link.href = domainLogo;
link.rel = "shortcut icon";
document.getElementsByTagName("head")[0].appendChild(link);
this.applyFavicon(domainIcon || domainLogo);
window.document.title = siteName + " - 运营后台";
});
},

View File

@@ -1,24 +1,29 @@
<template>
<div class="lang-icon">
<Dropdown @on-click="langChange">
<Icon type="md-globe" size="26"/>
<DropdownMenu slot="list">
<DropdownItem name="zh-CN">简体中文</DropdownItem>
<DropdownItem name="en-US">English</DropdownItem>
</DropdownMenu>
</Dropdown>
<el-dropdown @command="langChange">
<el-icon :size="26"><Platform /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="zh-CN">简体中文</el-dropdown-item>
<el-dropdown-item command="en-US">English</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<script>
import { Platform } from "@element-plus/icons-vue";
export default {
name: "langSwitch",
components: { Platform },
methods: {
langChange(v) {
this.$i18n.locale = v;
this.$store.commit("switchLang", v);
}
}
},
},
};
</script>

View File

@@ -1,81 +1,98 @@
<template>
<div class="message-con">
<Dropdown trigger="click">
<a href="javascript:void(0)">
<el-dropdown trigger="click" popper-class="todo-dropdown" @command="navigateTo">
<a href="javascript:void(0)" class="message-link">
{{ value > 0 ? "有" + value + "条待办事项" : "无待办事项" }}
<Icon v-if="value!=0" type="ios-arrow-down"></Icon>
<el-icon v-if="value != 0"><ArrowDown /></el-icon>
</a>
<DropdownMenu v-if="value!=0" slot="list">
<DropdownItem v-if="res.balanceCash" @click.native="navigateTo('deposit')">
<Badge :count="res.balanceCash">待处理预存款提现申请 </Badge>
</DropdownItem>
<DropdownItem v-if="res.complain" @click.native="navigateTo('orderComplaint')">
<Badge :count="res.complain">待处理投诉审核 </Badge>
</DropdownItem>
<DropdownItem v-if="res.distributionCash" @click.native="navigateTo('distributionCash')">
<Badge :count="res.distributionCash">待处理分销商提现申请 </Badge>
</DropdownItem>
<DropdownItem v-if="res.goods" @click.native="navigateTo('applyGoods')">
<Badge :count="res.goods">待处理商品审核 </Badge>
</DropdownItem>
<DropdownItem v-if="res.refund" @click.native="navigateTo('afterSaleOrder')">
<Badge :count="res.refund">待处理售后申请 </Badge>
</DropdownItem>
<DropdownItem v-if="res.store" @click.native="navigateTo('shopAuth')">
<Badge :count="res.store">待处理店铺入驻审核 </Badge>
</DropdownItem>
<DropdownItem v-if="res.waitPayBill" @click.native="navigateTo('accountStatementBill')">
<Badge :count="res.waitPayBill">待与商家对账</Badge>
</DropdownItem>
<div></div>
</DropdownMenu>
</Dropdown>
<template #dropdown>
<el-dropdown-menu v-if="value != 0">
<el-dropdown-item v-if="res.balanceCash" command="deposit">
<el-badge :value="res.balanceCash">待处理预存款提现申请</el-badge>
</el-dropdown-item>
<el-dropdown-item v-if="res.complain" command="orderComplaint">
<el-badge :value="res.complain">待处理投诉审核</el-badge>
</el-dropdown-item>
<el-dropdown-item v-if="res.distributionCash" command="distributionCash">
<el-badge :value="res.distributionCash">待处理分销商提现申请</el-badge>
</el-dropdown-item>
<el-dropdown-item v-if="res.goods" command="applyGoods">
<el-badge :value="res.goods">待处理商品审核</el-badge>
</el-dropdown-item>
<el-dropdown-item v-if="res.refund" command="afterSaleOrder">
<el-badge :value="res.refund">待处理售后申请</el-badge>
</el-dropdown-item>
<el-dropdown-item v-if="res.store" command="shopAuth">
<el-badge :value="res.store">待处理店铺入驻审核</el-badge>
</el-dropdown-item>
<el-dropdown-item v-if="res.waitPayBill" command="accountStatementBill">
<el-badge :value="res.waitPayBill">待与商家对账</el-badge>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<script>
import { ArrowDown } from "@element-plus/icons-vue";
export default {
name: "messageTip",
data() {
return {
value: 0, // 消息数量
empty: false, // 是否为空
};
},
components: { ArrowDown },
props: {
res: {
type: null,
type: Object,
default: () => ({}),
},
},
mounted() {
this.init();
computed: {
value() {
let count = 0;
const r = this.res || {};
Object.keys(r).forEach((k) => {
if (r[k]) count += r[k];
});
return count;
},
},
methods: {
navigateTo(name) {
this.$router.push({
name,
});
},
init() {
Object.keys(this.res).forEach((item) => {
this.value = parseInt(this.value) + parseInt(this.res[item]);
});
this.$router.push({ name });
},
},
};
</script>
<style scoped lang="scss">
::v-deep .ivu-select-dropdown {
text-align: left;
}
.message-con {
margin-right: 10px;
}
::v-deep .ivu-dropdown-item{
padding: 7px 20px !important;
}
::v-deep .ivu-badge-count{
right: -10px !important;
<style scoped>
.message-link {
color: inherit;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 4px;
}
</style>
<style>
.todo-dropdown .el-dropdown-menu__item {
min-width: 150px;
padding: 6px 36px 6px 16px;
line-height: 1.6;
}
.todo-dropdown .el-dropdown-menu__item + .el-dropdown-menu__item {
margin-top: 4px;
}
.todo-dropdown .el-badge {
width: 100%;
}
.todo-dropdown .el-badge__content.is-fixed {
right: 4px;
top: 8px;
transform: translateY(-50%) translateX(50%);
}
</style>

View File

@@ -0,0 +1,201 @@
<template>
<el-breadcrumb separator=">" class="page-breadcrumb">
<el-breadcrumb-item
v-for="(item, idx) in items"
:key="idx"
>
<a
v-if="idx < items.length - 1 && item.target"
class="bc-link"
@click.prevent="go(item.target)"
>
{{ item.title }}
</a>
<span v-else :class="{ 'is-current': idx === items.length - 1 }">
{{ item.title }}
</span>
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script>
import { otherRouter } from "@/router/router";
const stripLayout = (name) => (name || "").replace(/__layout$/, "");
// 详情/二级页面归属的「列表/父页面」name 映射
const PARENT_MAP = {
"member-detail": "memberList",
"order-detail": "orderList",
"after-order-detail": "afterSale",
"order-complaint-detail": "orderComplaint",
"shop-detail": "shopList",
"shop-operation": "shopList",
"bill-detail": "bill",
"goods-detail": "manager-goods",
"goods-parameter-edit": "goods-parameter",
"add-points-goods": "manager-points-goods",
"edit-points-goods": "manager-points-goods",
"coupon-receive": "manager-coupon",
"add-platform-coupon": "manager-coupon",
"edit-platform-coupon": "manager-coupon",
"add-coupon-activity": "manager-coupon-activity",
"edit-coupon-activity": "manager-coupon-activity",
"coupon-activity-info": "manager-coupon-activity",
"pintuan-goods": "manager-pintuan",
"full-discount-detail": "manager-full-discount",
"manager-seckill-add": "manager-seckill",
"add-kanJia-activity-goods": "manager-kanjia",
"edit-kanJia-activity-goods": "manager-kanjia",
"add-gift-card-cash-activity": "manager-gift-card-cash",
"edit-gift-card-cash-activity": "manager-gift-card-cash",
"gift-card-cash-records": "manager-gift-card-cash",
"gift-card-cash-batch-credentials": "manager-gift-card-cash",
};
const findFirstLeaf = (group) => {
if (!group || !Array.isArray(group.children) || !group.children.length) {
return null;
}
const first = group.children.find((c) => c && c.name) || group.children[0];
return stripLayout(first?.name) || null;
};
export default {
name: "pageBreadcrumb",
computed: {
items() {
const route = this.$route;
if (!route || !route.name || route.name === "home_index") {
return [{ title: "首页" }];
}
return this.buildChain(stripLayout(route.name));
},
},
methods: {
go(target) {
if (!target || !target.name) return;
if (this.$route.name === target.name) return;
this.$router.push(target);
},
findInMenu(name) {
const menuList = this.$store.state.app.menuList || [];
for (const grp of menuList) {
for (const child of grp.children || []) {
if (stripLayout(child.name) === name) {
return { group: grp, child };
}
}
}
return null;
},
routerEntry(name) {
if (Array.isArray(otherRouter.children)) {
return otherRouter.children.find((c) => c.name === name);
}
return null;
},
topItem() {
const navList = this.$store.state.app.navList || [];
const menuList = this.$store.state.app.menuList || [];
const currNavTitle = this.$store.state.app.currNavTitle;
const firstName =
this.$route.meta && this.$route.meta.firstRouterName;
let topTitle = currNavTitle;
if (firstName) {
const top = navList.find((n) => n.name === firstName);
if (top) topTitle = top.title;
}
if (!topTitle) return null;
let target = null;
if (menuList && menuList.length) {
const leafName = findFirstLeaf(menuList[0]);
if (leafName) target = { name: leafName };
}
return { title: topTitle, target };
},
buildChain(name, depth = 0) {
if (depth > 6) return [];
const inMenu = this.findInMenu(name);
if (inMenu) {
const list = [];
const top = this.topItem();
if (top) list.push(top);
const groupLeaf = findFirstLeaf(inMenu.group);
list.push({
title: inMenu.group.title,
target: groupLeaf ? { name: groupLeaf } : null,
});
list.push({ title: inMenu.child.title });
return list;
}
const parentName = PARENT_MAP[name];
const entry = this.routerEntry(name);
const selfTitle = entry?.title
? entry.title
: (this.$route.meta && this.$route.meta.title
? String(this.$route.meta.title).replace(/\s*-\s*.+$/, "")
: "");
if (parentName) {
const parentChain = this.buildChain(parentName, depth + 1).map(
(it, idx, arr) =>
idx === arr.length - 1
? { title: it.title, target: { name: parentName } }
: it
);
if (selfTitle) parentChain.push({ title: selfTitle });
return parentChain;
}
const list = [];
const top = this.topItem();
if (top) list.push(top);
if (selfTitle) list.push({ title: selfTitle });
return list;
},
},
};
</script>
<style lang="scss" scoped>
.page-breadcrumb {
padding: 0 !important;
margin: 0 !important;
line-height: 44px;
font-size: 14px;
display: flex;
align-items: center;
:deep(.el-breadcrumb__item) {
.el-breadcrumb__inner {
color: #606266;
font-weight: 400;
}
&:first-child {
margin-left: 0 !important;
padding-left: 0 !important;
}
}
.bc-link {
color: #606266;
cursor: pointer;
text-decoration: none;
&:hover {
color: $theme_color;
}
}
.is-current {
color: $theme_color;
font-weight: 500;
}
}
</style>

View File

@@ -1,27 +1,32 @@
<template>
<div class="ivu-shrinkable-menu">
<!-- 一级菜单 -->
<Menu ref="sideMenu" width="80px" theme="dark" :active-name="currNav" @on-select="selectNav">
<MenuItem v-for="(item, i) in navList" :key="i" :name="item.name">
{{item.title}}
</MenuItem>
</Menu>
<!-- 二级菜单 -->
<Menu
ref="childrenMenu"
:active-name="$route.name"
width="120px"
@on-select="changeMenu"
<div class="shrinkable-menu">
<el-menu
class="nav-menu-dark"
:default-active="currNav"
@select="selectNav"
>
<el-menu-item v-for="(item, i) in navList" :key="i" :index="item.name">
{{ item.title }}
</el-menu-item>
</el-menu>
<el-menu
:key="currNav"
class="sub-menu"
:default-active="$route.name"
@select="changeMenu"
>
<template v-for="item in menuList" :key="item.id">
<el-menu-item-group :title="item.title">
<el-menu-item
v-for="menu in item.children"
:key="menu.name"
:index="menu.name"
>
<template v-for="item in menuList">
<MenuGroup :title="item.title" :key="item.id" style="padding-left:0;">
<MenuItem :name="menu.name" v-for="menu in item.children" :key="menu.name">
{{ menu.title }}
</MenuItem>
</MenuGroup>
</el-menu-item>
</el-menu-item-group>
</template>
</Menu>
</el-menu>
</div>
</template>
@@ -39,60 +44,80 @@ export default {
},
currNav() {
return this.$store.state.app.currNav;
}
},
},
watch: {
// 监听路由变化
$route: {
handler: function (val, oldVal) {
if (val.meta.firstRouterName && val.meta.firstRouterName !== this.currNav) {
this.selectNav(val.meta.firstRouterName)
}
}
$route(val) {
if (
val.meta.firstRouterName &&
val.meta.firstRouterName !== this.currNav
) {
this.selectNav(val.meta.firstRouterName);
}
},
},
methods: {
changeMenu(name) { //二级路由点击
this.$router.push({
name: name
changeMenu(name) {
if (!name) return;
if (this.$router.hasRoute(name)) {
this.$router.push({ name });
return;
}
util.initRouter(this);
this.$nextTick(() => {
if (this.$router.hasRoute(name)) {
this.$router.push({ name });
} else {
this.$Message.warning("菜单路由未就绪,请刷新页面后重试");
}
});
},
selectNav(name) { // 一级路由点击事件
selectNav(name) {
this.$store.commit("setCurrNav", name);
this.setStore("currNav", name);
util.initRouter(this);
this.$nextTick(()=>{
this.$refs.childrenMenu.updateActiveName()
})
},
}
},
};
</script>
<style lang="scss" scoped>
.ivu-shrinkable-menu{
height: calc(100% - 60px);
.shrinkable-menu {
height: 100%;
width: 200px;
display: flex;
}
.ivu-btn-text:hover {
background-color: rgba(255,255,255,.2) !important;
.nav-menu-dark {
width: 80px;
background-color: #191a23;
border-right: none;
:deep(.el-menu-item) {
color: rgba(255, 255, 255, 0.7);
justify-content: center;
padding: 0 8px !important;
text-align: center;
line-height: 1.3;
height: auto;
min-height: 56px;
white-space: normal;
}
.ivu-menu-dark.ivu-menu-vertical .ivu-menu-item-active:not(.ivu-menu-submenu), .ivu-menu-dark.ivu-menu-vertical .ivu-menu-submenu-title-active:not(.ivu-menu-submenu){
background-color: #fff;
&:hover{
background-color: #fff;
:deep(.el-menu-item.is-active) {
background-color: #fff !important;
color: $theme_color !important;
}
}
.ivu-menu-vertical{
.sub-menu {
width: 120px;
overflow-y: auto;
}
.ivu-menu-dark.ivu-menu-vertical .ivu-menu-item-active:not(.ivu-menu-submenu), .ivu-menu-dark.ivu-menu-vertical .ivu-menu-submenu-title-active:not(.ivu-menu-submenu){
color: $theme_color;
}
::v-deep.ivu-menu-vertical .ivu-menu-item-group-title {
height: 40px;
border-right: none;
:deep(.el-menu-item-group__title) {
padding-left: 16px;
line-height: 40px;
padding-left: 20px;
}
}
</style>

View File

@@ -1,31 +1,42 @@
<template>
<div
ref="scrollCon"
@DOMMouseScroll="handlescroll"
@mousewheel="handlescroll"
class="tags-outer-scroll-con"
>
<ul v-show="visible" :style="{left: contextMenuLeft + 'px', top: contextMenuTop + 'px'}" class="contextmenu">
<li v-for="(item, key) of actionList" @click="handleTagsOption(key)" :key="key">{{item}}</li>
<ul
v-show="visible"
:style="{ left: contextMenuLeft + 'px', top: contextMenuTop + 'px' }"
class="contextmenu"
>
<li
v-for="(item, key) of actionList"
:key="key"
@click="handleTagsOption(key)"
>
{{ item }}
</li>
</ul>
<div ref="scrollBody" class="tags-inner-scroll-body" :style="{left: tagBodyLeft + 'px'}">
<transition-group name="taglist-moving-animation">
<Tag
type="dot"
<div
ref="scrollBody"
class="tags-inner-scroll-body"
:style="{ left: tagBodyLeft + 'px' }"
>
<el-tag
v-for="item in pageTagsList"
ref="tagsPageOpened"
:key="item.name"
:name="item.name"
@on-close="closePage"
@click.native="linkTo(item)"
:closable="item.name=='home_index'?false:true"
:color="item.children?(item.children[0].name==currentPageName?'primary':'default'):(item.name==currentPageName?'primary':'default')"
@contextmenu.prevent.native="contextMenu(item, $event)"
>{{ itemTitle(item) }}</Tag>
</transition-group>
:closable="item.name !== 'home_index'"
:type="tagType(item)"
:effect="isActive(item) ? 'dark' : 'plain'"
class="page-tag"
size="large"
@close="closePage($event, item.name)"
@click="linkTo(item)"
@contextmenu.prevent="contextMenu(item, $event)"
>
{{ itemTitle(item) }}
</el-tag>
</div>
</div>
</template>
@@ -33,101 +44,91 @@
<script>
export default {
name: "tagsPageOpened",
data() {
return {
currentPageName: this.$route.name, // 当前路由名称
tagBodyLeft: 0, // 标签左偏移量
visible: false, // 显示操作按钮
contextMenuLeft: 0, // 内容左偏移量
contextMenuTop: 0, // 内容上偏移量
actionList: {
others: '关闭其他',
clearAll: '关闭所有'
},
refsTag: [], // 所有已打开标签
tagsCount: 1 // 标签数量
};
},
props: {
pageTagsList: Array,
beforePush: {
type: Function,
default: item => {
return true;
}
}
default: () => true,
},
computed: {
title() {
return this.$store.state.app.currentTitle;
},
tagsList() {
return this.$store.state.app.pageOpenedList;
}
data() {
return {
currentPageName: this.$route.name,
tagBodyLeft: 0,
visible: false,
contextMenuLeft: 0,
contextMenuTop: 0,
actionList: {
others: "关闭其他",
clearAll: "关闭所有",
},
};
},
methods: {
isActive(item) {
return item.children
? item.children[0].name === this.currentPageName
: item.name === this.currentPageName;
},
tagType(item) {
return this.isActive(item) ? "primary" : "info";
},
itemTitle(item) {
if (typeof item.title == "object") {
if (typeof item.title === "object") {
return this.$t(item.title.i18n);
} else {
return item.title;
}
return item.title;
},
closePage(event, name) {
let pageOpenedList = this.$store.state.app.pageOpenedList;
let lastPageObj = pageOpenedList[0];
if (this.currentPageName == name) {
let len = pageOpenedList.length;
if (this.currentPageName === name) {
const len = pageOpenedList.length;
for (let i = 1; i < len; i++) {
if (pageOpenedList[i].name == name) {
if (i < len - 1) {
lastPageObj = pageOpenedList[i + 1];
} else {
lastPageObj = pageOpenedList[i - 1];
}
if (pageOpenedList[i].name === name) {
lastPageObj =
i < len - 1 ? pageOpenedList[i + 1] : pageOpenedList[i - 1];
break;
}
}
} else {
let tagWidth = event.target.parentNode.offsetWidth;
} else if (event && event.target) {
const tagWidth = event.target.parentNode?.offsetWidth || 0;
this.tagBodyLeft = Math.min(this.tagBodyLeft + tagWidth, 0);
}
this.$store.commit("removeTag", name);
this.$store.commit("closePage", name);
pageOpenedList = this.$store.state.app.pageOpenedList;
localStorage.pageOpenedList = JSON.stringify(pageOpenedList);
if (this.currentPageName == name) {
if (this.currentPageName === name) {
this.linkTo(lastPageObj);
}
},
linkTo(item) {
if (this.$route.name == item.name) {
return;
}
let routerObj = {};
routerObj.name = item.name;
if (item.argu) {
routerObj.params = item.argu;
}
if (item.query) {
routerObj.query = item.query;
}
if (this.$route.name === item.name) return;
const routerObj = { name: item.name };
if (item.argu) routerObj.params = item.argu;
if (item.query) routerObj.query = item.query;
if (this.beforePush(item)) {
this.$router.push(routerObj);
}
},
handlescroll(e) {
var type = e.type;
const type = e.type;
let delta = 0;
if (type == "DOMMouseScroll" || type == "mousewheel") {
if (type === "DOMMouseScroll" || type === "mousewheel") {
delta = e.wheelDelta ? e.wheelDelta : -(e.detail || 0) * 40;
}
let left = 0;
if (delta > 0) {
left = Math.min(0, this.tagBodyLeft + delta);
} else {
if ( this.$refs.scrollCon.offsetWidth - 100 < this.$refs.scrollBody.offsetWidth ) {
if ( this.tagBodyLeft < -( this.$refs.scrollBody.offsetWidth - this.$refs.scrollCon.offsetWidth + 100 ) ) {
} else if (
this.$refs.scrollCon.offsetWidth - 100 <
this.$refs.scrollBody.offsetWidth
) {
if (
this.tagBodyLeft <
-(this.$refs.scrollBody.offsetWidth - this.$refs.scrollCon.offsetWidth + 100)
) {
left = this.tagBodyLeft;
} else {
left = Math.max(
@@ -140,90 +141,40 @@ export default {
} else {
this.tagBodyLeft = 0;
}
}
this.tagBodyLeft = left;
},
handleTagsOption(type) {
if (type == "clearAll") {
if (type === "clearAll") {
this.$store.commit("clearAllTags");
this.$router.push({
name: "home_index"
});
this.$router.push({ name: "home_index" });
} else {
this.$store.commit("clearOtherTags", this);
}
this.tagBodyLeft = 0;
},
moveToView(tag) {
if (tag.offsetLeft < -this.tagBodyLeft) {
// 标签在可视区域左侧
this.tagBodyLeft = -tag.offsetLeft + 10;
} else if (
tag.offsetLeft + 10 > -this.tagBodyLeft &&
tag.offsetLeft + tag.offsetWidth <
-this.tagBodyLeft + this.$refs.scrollCon.offsetWidth - 100
) {
// 标签在可视区域
this.tagBodyLeft = Math.min(
0,
this.$refs.scrollCon.offsetWidth -
100 -
tag.offsetWidth -
tag.offsetLeft -
20
);
} else {
// 标签在可视区域右侧
this.tagBodyLeft = -(
tag.offsetLeft -
(this.$refs.scrollCon.offsetWidth - 100 - tag.offsetWidth) +
20
);
}
},
contextMenu(item, e) {
this.visible = true
const offsetLeft = this.$el.getBoundingClientRect().left
this.contextMenuLeft = e.clientX - offsetLeft + 10
this.contextMenuTop = e.clientY - 64
this.visible = true;
const offsetLeft = this.$el.getBoundingClientRect().left;
this.contextMenuLeft = e.clientX - offsetLeft + 10;
this.contextMenuTop = e.clientY - 64;
},
closeMenu() {
this.visible = false
}
this.visible = false;
},
mounted() {
this.refsTag = this.$refs.tagsPageOpened;
setTimeout(() => {
this.refsTag.forEach((item, index) => {
if (this.$route.name == item.name) {
let tag = this.refsTag[index].$el;
this.moveToView(tag);
}
});
}, 1); // 这里不设定时器就会有偏移bug
this.tagsCount = this.tagsList.length;
},
watch: {
$route(to) {
this.currentPageName = to.name;
this.$nextTick(() => {
this.refsTag.forEach((item, index) => {
if (to.name == item.name) {
let tag = this.refsTag[index].$el;
this.moveToView(tag);
}
});
});
this.tagsCount = this.tagsList.length;
this.tagsCount = this.pageTagsList?.length;
},
visible(value) {
if (value) {
document.body.addEventListener('click', this.closeMenu)
document.body.addEventListener("click", this.closeMenu);
} else {
document.body.removeEventListener('click', this.closeMenu)
}
}
document.body.removeEventListener("click", this.closeMenu);
}
},
},
};
</script>
@@ -237,7 +188,7 @@ export default {
z-index: 11000;
list-style-type: none;
border-radius: 4px;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .1);
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.1);
li {
margin: 0;
padding: 5px 15px;
@@ -247,7 +198,25 @@ export default {
}
}
}
.ivu-tag-primary, .ivu-tag-primary.ivu-tag-dot .ivu-tag-dot-inner{
background: $theme_color;
.page-tag {
margin-right: 8px;
cursor: pointer;
height: 28px;
padding: 0 12px;
border-radius: 4px;
font-size: 13px;
transition: all 0.2s ease;
& + .page-tag {
margin-left: 0;
}
&:hover {
opacity: 0.85;
}
.el-tag__close {
margin-left: 6px;
}
}
</style>

View File

@@ -24,14 +24,14 @@
}
.sidebar-menu-con {
height: 100%;
height: calc(100% - 60px);
position: fixed;
top: 0;
top: 60px;
left: 0;
z-index: 21;
transition: width 0.3s; // background: rgb(73, 80, 96)
background: #fff;
box-shadow: rgba(0, 21, 41, 0.35) 2px 0px 6px;
box-shadow: rgba(0, 21, 41, 0.12) 2px 0 6px;
}
.layout-text {
@@ -52,11 +52,11 @@
box-sizing: border-box;
position: fixed;
display: block;
padding-left: 200px;
padding-left: 0;
width: 100%;
height: 100px;
z-index: 20;
box-shadow: 0 2px 1px 1px rgba(100, 100, 100, 0.1);
height: 60px;
z-index: 22;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
transition: padding 0.3s;
}
@@ -81,22 +81,29 @@
}
.tags-con {
height: 40px;
height: 44px;
z-index: -1;
background: #f0f0f0;
background: #fff;
border-top: 1px solid #f0f0f0;
border-bottom: 1px solid #f0f0f0;
.tags-outer-scroll-con {
position: relative;
box-sizing: border-box;
padding-right: 120px;
padding-right: 12px;
width: 100%;
height: 100%;
display: flex;
align-items: center;
.tags-inner-scroll-body {
position: absolute;
padding: 2px 10px;
padding: 0 6px;
overflow: visible;
white-space: nowrap;
display: flex;
align-items: center;
height: 100%;
transition: left 0.3s ease;
}
@@ -119,15 +126,34 @@
min-width: 740px;
height: 60px;
background: #fff;
box-shadow: 0 2px 1px 1px rgba(100, 100, 100, 0.1);
position: relative;
z-index: 11;
display: flex;
align-items: center;
.navicon-con {
margin: 6px;
display: inline-block;
}
.header-logo-con {
flex-shrink: 0;
width: 200px;
height: 60px;
display: flex;
align-items: center;
justify-content: flex-start;
padding-left: 12px;
img {
display: block;
max-height: 44px;
max-width: 100%;
width: auto;
height: auto;
}
}
.header-middle-con {
position: absolute;
left: 0px;
@@ -146,7 +172,8 @@
display: flex;
align-items: center;
padding: 0 30px;
justify-content: space-between;
justify-content: flex-end;
margin-left: auto;
height: 100%;
@@ -162,9 +189,9 @@
.message-con {
display: inline-block;
padding: 18px 0;
display: inline-flex;
align-items: center;
height: 100%;
text-align: center;
cursor: pointer;
@@ -215,15 +242,29 @@
}
}
.breadcrumb-bar {
position: fixed;
top: 60px;
left: 212px;
right: 0;
height: 44px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
z-index: 19;
display: flex;
align-items: center;
padding: 0;
}
.single-page-con {
min-width: 740px;
position: relative;
left: 220px;
left: 200px;
top: 100px;
right: 0;
bottom: 0;
height: calc(100% - 110px);
width: calc(100% - 220px);
width: calc(100% - 200px);
overflow: auto;
background-color: #f0f0f0;
z-index: 1;
@@ -231,7 +272,7 @@
.single-page {
position: relative;
margin: 10px;
margin: 10px 12px;
}
}
@@ -247,15 +288,7 @@
}
.logo-con {
width: 100%;
height: 60px;
padding: 8px;
text-align: center;
border-bottom: 1px solid #eee;
img {
height: 44px;
width: auto;
}
display: none;
}
.menu-bar {

View File

@@ -1,193 +1,150 @@
<template>
<div class="search">
<Card>
<Form
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
:label-width="70"
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<Form-item label="会员名称" prop="memberName">
<Input
type="text"
<el-form-item label="会员名称" prop="memberName">
<el-input
v-model="searchForm.memberName"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="充值单号" prop="rechargeSn">
<Input
type="text"
</el-form-item>
<el-form-item label="充值单号" prop="rechargeSn">
<el-input
v-model="searchForm.rechargeSn"
placeholder="请输入充值单号"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="支付时间">
<DatePicker
</el-form-item>
<el-form-item label="支付时间">
<el-date-picker
v-model="selectDate"
type="datetimerange"
format="yyyy-MM-dd HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
clearable
@on-change="selectDateRange"
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="选择起始时间"
style="width: 240px"
></DatePicker>
</Form-item>
<Button @click="handleSearch" type="primary" icon="ios-search" class="search-btn">搜索</Button>
</Form>
</Card>
<Card>
<Table
:loading="loading"
style="width: 360px"
@change="selectDateRange"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<el-table
v-loading="loading"
border
:columns="columns"
:data="data"
ref="table"
class="mt_10"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
style="width: 100%"
>
<el-table-column prop="memberName" label="会员名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="rechargeSn" label="订单号" min-width="180" show-overflow-tooltip />
<el-table-column label="充值金额" width="160" sortable>
<template #default="{ row }">
<priceColorScheme v-if="row" :value="row.rechargeMoney" :color="$mainColor" unit="+" />
</template>
</el-table-column>
<el-table-column label="充值方式" width="120">
<template #default="{ row }">
<span v-if="row">{{ rechargeWayText(row.rechargeWay) }}</span>
</template>
</el-table-column>
<el-table-column label="支付状态" width="120">
<template #default="{ row }">
<el-tag v-if="row" :type="row.payStatus === 'PAID' ? 'success' : 'danger'">
{{ row.payStatus === "PAID" ? "已付款" : "未付款" }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="充值时间" width="190" />
<el-table-column prop="payTime" label="支付时间" width="190" />
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</div>
</template>
<script>
import {
getUserRecharge,
} from "@/api/member";
import { getUserRecharge } from "@/api/member";
export default {
name: "recharge",
data() {
return {
loading: true, // 表单加载状态
loading: true,
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
startDate: "", // 起始时间
endDate: "", // 终止时间
memberName:""
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
startDate: "",
endDate: "",
memberName: "",
},
selectDate: null, // 选择区间时间
columns: [
{
title: "会员名称",
key: "memberName",
minWidth: 120,
tooltip: true
},
{
title: "订单号",
key: "rechargeSn",
minWidth: 180,
tooltip: true
},
{
title: "充值金额",
key: "rechargeMoney",
width: 160,
sortable: true,
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.rechargeMoney,color:this.$mainColor,unit:"+"}} );
},
},
{
title: "充值方式",
key: "rechargeWay",
width: 120,
render: (h, params) => {
if (params.row.rechargeWay === 'ALIPAY') {
return h('div', [h('span', {}, '支付宝')]);
} else if (params.row.rechargeWay === 'WECHAT') {
return h('div', [h('span', {}, '微信')]);
} else if (params.row.rechargeWay === 'BANK_TRANSFER') {
return h('div', [h('span', {}, '线下转账')]);
} else {
return h('div', [h('span', {}, '')]);
}
}
},
{
title: "支付状态",
key: "payStatus",
align: "left",
width: 120,
sortable: false,
render: (h, params) => {
if (params.row.payStatus == "PAID") {
return h("Tag", {props: {color: "green",},}, "已付款");
} else {
return h("Tag", {props: {color: "red",},}, "未付款");
}
},
},
{
title: "充值时间",
key: "createTime",
align: "left",
width: 190,
sortable: false,
},
{
title: "支付时间",
key: "payTime",
align: "left",
width: 190,
sortable: false,
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
selectDate: null,
data: [],
total: 0,
};
},
methods: {
// 初始化数据
rechargeWayText(way) {
if (way === "ALIPAY") return "支付宝";
if (way === "WECHAT") return "微信";
if (way === "BANK_TRANSFER") return "线下转账";
return "";
},
init() {
this.getDataList();
},
// 分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
// 时间段赋值
selectDateRange(v) {
if (v) {
if (v && v.length === 2) {
this.searchForm.startDate = v[0];
this.searchForm.endDate = v[1];
} else {
this.searchForm.startDate = "";
this.searchForm.endDate = "";
}
},
// 获取列表数据
getDataList() {
this.loading = true;
getUserRecharge(this.searchForm).then((res) => {
@@ -197,8 +154,6 @@
this.total = res.result.total;
}
});
this.total = this.data.length;
this.loading = false;
},
},
mounted() {

View File

@@ -1,132 +1,148 @@
<template>
<div class="search">
<Card>
<Row @keydown.enter.native="handleSearch">
<Form ref="searchForm" :model="searchForm" inline :label-width="70" class="search-form">
<Form-item label="会员名称" prop="memberName">
<Input type="text" v-model="searchForm.memberName" placeholder="请输入会员名称" clearable style="width: 240px" />
</Form-item>
<Form-item label="支付时间">
<DatePicker v-model="selectDate" type="datetimerange" format="yyyy-MM-dd HH:mm:ss" clearable @on-change="selectDateRange" placeholder="选择起始时间" style="width: 240px"></DatePicker>
</Form-item>
<Button @click="handleSearch" type="primary" icon="ios-search" class="search-btn">搜索</Button>
</Form>
</Row>
</Card>
<Card>
<Table class="mt_10" :loading="loading" border :columns="columns" :data="data" ref="table"></Table>
<Row type="flex" justify="end" class="mt_10">
<Page :current="searchForm.pageNumber" :total="total" :page-size="searchForm.pageSize" @on-change="changePage" @on-page-size-change="changePageSize" :page-size-opts="[20, 50, 100]"
size="small" show-total show-elevator show-sizer></Page>
</Row>
</Card>
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<el-form-item label="会员名称" prop="memberName">
<el-input
v-model="searchForm.memberName"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item label="支付时间">
<el-date-picker
v-model="selectDate"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
clearable
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="选择起始时间"
style="width: 360px"
@change="selectDateRange"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<el-table
v-loading="loading"
border
:data="data"
ref="table"
class="mt_10"
style="width: 100%"
>
<el-table-column prop="memberName" label="会员名称" min-width="100" />
<el-table-column label="变动金额" width="150">
<template #default="{ row }">
<template v-if="row">
<priceColorScheme
v-if="row.money > 0"
:value="row.money"
color="green"
/>
<priceColorScheme
v-else-if="row.money < 0"
:value="row.money"
:color="$mainColor"
/>
</template>
</template>
</el-table-column>
<el-table-column prop="createTime" label="变更时间" width="200" />
<el-table-column label="业务类型" width="200">
<template #default="{ row }">
<span v-if="row">{{ serviceTypeText(row.serviceType) }}</span>
</template>
</el-table-column>
<el-table-column prop="detail" label="详细" min-width="300" show-overflow-tooltip />
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</div>
</template>
<script>
import { getUserWallet } from "@/api/member";
export default {
name: "walletLog",
data() {
return {
loading: true, // 表单加载状态
loading: true,
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
startDate: "", // 起始时间
endDate: "", // 终止时间
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
startDate: "",
endDate: "",
memberName: "",
},
selectDate: null, // 选择时间段
columns: [
// 表头
{
title: "会员名称",
key: "memberName",
minWidth: 100,
},
{
title: "变动金额",
key: "money",
width: 150,
render: (h, params) => {
if (params.row.money >0) {
return h("priceColorScheme", {props:{value:params.row.money,color:'green'}} );
} else if (params.row.money < 0) {
return h("priceColorScheme", {props:{value:params.row.money,color:this.$mainColor}} );
}
},
},
{
title: "变更时间",
key: "createTime",
width: 200,
},
{
title: "业务类型",
key: "serviceType",
width: 200,
render: (h, params) => {
if (params.row.serviceType == "WALLET_WITHDRAWAL") {
return h("div", [h("span", {}, "余额提现")]);
} else if (params.row.serviceType == "WALLET_PAY") {
return h("div", [h("span", {}, "余额支付")]);
} else if (params.row.serviceType == "WALLET_REFUND") {
return h("div", [h("span", {}, "余额退款")]);
} else if (params.row.serviceType == "WALLET_RECHARGE") {
return h("div", [h("span", {}, "余额充值")]);
} else {
return h("div", [h("span", {}, "佣金提成")]);
}
},
},
{
title: "详细",
key: "detail",
minWidth: 300,
tooltip: true,
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
selectDate: null,
data: [],
total: 0,
};
},
methods: {
// 初始化数据
serviceTypeText(type) {
if (type === "WALLET_WITHDRAWAL") return "余额提现";
if (type === "WALLET_PAY") return "余额支付";
if (type === "WALLET_REFUND") return "余额退款";
if (type === "WALLET_RECHARGE") return "余额充值";
return "佣金提成";
},
init() {
this.getDataList();
},
// 分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
// 时间段赋值
selectDateRange(v) {
if (v) {
if (v && v.length === 2) {
this.searchForm.startDate = v[0];
this.searchForm.endDate = v[1];
} else {
this.searchForm.startDate = "";
this.searchForm.endDate = "";
}
},
// 获取列表数据
getDataList() {
this.loading = true;
getUserWallet(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
@@ -134,8 +150,6 @@ export default {
this.total = res.result.total;
}
});
this.total = this.data.length;
this.loading = false;
},
},
mounted() {

View File

@@ -1,102 +1,171 @@
<template>
<div class="search">
<Card>
<Row @keydown.enter.native="handleSearch">
<Form ref="searchForm" :model="searchForm" inline :label-width="70" class="search-form">
<Form-item label="会员名称" prop="memberName">
<Input type="text" v-model="searchForm.memberName" placeholder="请输入会员名称" clearable style="width: 240px" />
</Form-item>
<Form-item label="提现状态" prop="applyStatus">
<Select v-model="searchForm.applyStatus" clearable style="width: 240px">
<Option value="APPLY">申请中</Option>
<Option value="VIA_AUDITING">审核通过</Option>
<Option value="WAIT_USER_CONFIRM">用户确认</Option>
<Option value="FAIL_AUDITING">审核拒绝</Option>
<Option value="SUCCESS">提现成功</Option>
<Option value="ERROR">提现失败</Option>
</Select>
</Form-item>
<Form-item label="申请时间">
<DatePicker v-model="selectDate" type="datetimerange" format="yyyy-MM-dd HH:mm:ss" clearable @on-change="selectDateRange" placeholder="选择起始时间" style="width: 240px"></DatePicker>
</Form-item>
<Form-item style="margin-left: -35px" class="br">
<Button @click="handleSearch" type="primary" icon="ios-search">搜索
</Button>
</Form-item>
</Form>
</Row>
</Card>
<Card>
<Table class="mt_10" :loading="loading" border :columns="columns" :data="data" ref="table" sortable="custom" @on-sort-change="changeSort" @on-selection-change="changeSelect"></Table>
<Row type="flex" justify="end" class="mt_10">
<Page :current="searchForm.pageNumber" :total="total" :page-size="searchForm.pageSize" @on-change="changePage" @on-page-size-change="changePageSize" :page-size-opts="[20, 50, 100]" size="small"
show-total show-elevator show-sizer></Page>
</Row>
</Card>
<Modal :title="modalTitle" v-model="roleModalVisible" :mask-closable="false" :width="500">
<Form :label-width="80">
<FormItem label="申请编号">
<span>{{showList.sn}}</span>
</FormItem>
<FormItem label="用户名称">
<span>{{showList.memberName}}</span>
</FormItem>
<FormItem label="申请金额">
<priceColorScheme :value="showList.applyMoney" :color="$mainColor"></priceColorScheme>
</FormItem>
<FormItem label="提现状态">
<span>{{showList.applyStatus | paramTypeFilter}}</span>
</FormItem>
<FormItem label="申请时间">
<span>{{showList.createTime}}</span>
</FormItem>
<FormItem label="审核备注">
<Input v-model="audit" type="textarea" />
</FormItem>
</Form>
<div slot="footer" v-if="showList.applyStatus == 'APPLY'">
<Button type="text" @click="submitRole(false)">拒绝</Button>
<Button type="primary" :loading="submitLoading" @click="submitRole(true)">通过
</Button>
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<el-form-item label="会员名称" prop="memberName">
<el-input
v-model="searchForm.memberName"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item label="提现状态" prop="applyStatus">
<el-select v-model="searchForm.applyStatus" clearable style="width: 240px">
<el-option label="申请中" value="APPLY" />
<el-option label="审核通过" value="VIA_AUDITING" />
<el-option label="用户确认" value="WAIT_USER_CONFIRM" />
<el-option label="审核拒绝" value="FAIL_AUDITING" />
<el-option label="提现成功" value="SUCCESS" />
<el-option label="提现失败" value="ERROR" />
</el-select>
</el-form-item>
<el-form-item label="申请时间">
<el-date-picker
v-model="selectDate"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
clearable
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="选择起始时间"
style="width: 360px"
@change="selectDateRange"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card>
<el-table
v-loading="loading"
border
:data="data"
ref="table"
class="mt_10"
style="width: 100%"
@sort-change="changeSort"
@selection-change="changeSelect"
>
<el-table-column prop="sn" label="申请编号" min-width="160" show-overflow-tooltip />
<el-table-column prop="memberName" label="用户名称" min-width="120" show-overflow-tooltip />
<el-table-column label="申请金额" width="120">
<template #default="{ row }">
<priceColorScheme v-if="row" :value="row.applyMoney" :color="$mainColor" />
</template>
</el-table-column>
<el-table-column label="提现状态" width="120">
<template #default="{ row }">
<el-tag v-if="row" :type="applyStatusTagType(row.applyStatus)">
{{ applyStatusText(row.applyStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="申请时间" width="170" sortable="custom" />
<el-table-column prop="inspectTime" label="审核时间" width="170" />
<el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="{ row }">
<div v-if="row" class="ops">
<a class="link-text" v-if="row.applyStatus === 'APPLY'" @click="openAudit(row)">审核</a>
<a class="link-text" v-else @click="openQuery(row)">查看</a>
</div>
</Modal>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<Modal :title="modalTitle" v-model="queryModalVisible" :mask-closable="false" :width="500">
<Form :label-width="80">
<FormItem label="申请编号:">
<el-dialog
v-model="roleModalVisible"
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
>
<el-form label-width="80px">
<el-form-item label="申请编号">
<span>{{ showList.sn }}</span>
</FormItem>
<FormItem label="用户名称">
</el-form-item>
<el-form-item label="用户名称">
<span>{{ showList.memberName }}</span>
</FormItem>
<FormItem label="申请金额">
<priceColorScheme :value="showList.applyMoney" :color="$mainColor"></priceColorScheme>
</FormItem>
<FormItem label="提现状态:">
<span>{{showList.applyStatus | paramTypeFilter}}</span>
</FormItem>
<FormItem label="申请时间:">
</el-form-item>
<el-form-item label="申请金额">
<priceColorScheme :value="showList.applyMoney" :color="$mainColor" />
</el-form-item>
<el-form-item label="提现状态">
<span>{{ paramTypeFilter(showList.applyStatus) }}</span>
</el-form-item>
<el-form-item label="申请时间">
<span>{{ showList.createTime }}</span>
</FormItem>
<FormItem label="审核时间:">
</el-form-item>
<el-form-item label="审核备注">
<el-input v-model="audit" type="textarea" />
</el-form-item>
</el-form>
<template #footer>
<template v-if="showList.applyStatus === 'APPLY'">
<el-button @click="submitRole(false)">拒绝</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitRole(true)">通过</el-button>
</template>
</template>
</el-dialog>
<el-dialog
v-model="queryModalVisible"
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
>
<el-form label-width="100px">
<el-form-item label="申请编号:">
<span>{{ showList.sn }}</span>
</el-form-item>
<el-form-item label="用户名称:">
<span>{{ showList.memberName }}</span>
</el-form-item>
<el-form-item label="申请金额:">
<priceColorScheme :value="showList.applyMoney" :color="$mainColor" />
</el-form-item>
<el-form-item label="提现状态:">
<span>{{ paramTypeFilter(showList.applyStatus) }}</span>
</el-form-item>
<el-form-item label="申请时间:">
<span>{{ showList.createTime }}</span>
</el-form-item>
<el-form-item label="审核时间:">
<span>{{ showList.inspectTime }}</span>
</FormItem>
<FormItem label="审核备注:">
<span>{{showList.inspectRemark || '暂无备注'}}</span>
</FormItem>
</Form>
<div slot="footer" v-if="showList.applyStatus == 'APPLY'">
<Button type="text" @click="submitRole(false)">拒绝</Button>
<Button type="primary" :loading="submitLoading" @click="submitRole(true)">通过
</Button>
</div>
<div slot="footer" v-else>
<Button type="text" @click="queryModalVisible = false">取消</Button>
</div>
</Modal>
</el-form-item>
<el-form-item label="审核备注:">
<span>{{ showList.inspectRemark || "暂无备注" }}</span>
</el-form-item>
</el-form>
<template #footer>
<template v-if="showList.applyStatus === 'APPLY'">
<el-button @click="submitRole(false)">拒绝</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitRole(true)">通过</el-button>
</template>
<el-button v-else @click="queryModalVisible = false">取消</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -105,169 +174,68 @@ import { getUserWithdrawApply, withdrawApply } from "@/api/member";
export default {
name: "withdrawApply",
components: {},
data() {
return {
modalTitle: "", //弹出框标题
openSearch: true, // 显示搜索
openTip: true, // 显示提示
loading: true, // 表单加载状态
audit: "", // 审核备注
roleModalVisible: false, // 审核模态框
queryModalVisible: false, // 审核模态框
modalTitle: "",
loading: true,
audit: "",
roleModalVisible: false,
queryModalVisible: false,
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
startDate: "", // 起始时间
endDate: "", // 终止时间
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
startDate: "",
endDate: "",
memberName: "",
applyStatus: "",
},
selectDate: null, // 选择时间段
submitLoading: false, // 添加或编辑提交状态
selectList: [], // 多选数据
selectCount: 0, // 多选计数
showList: {}, // 可操作选项
columns: [
{
title: "申请编号",
key: "sn",
align: "left",
tooltip: true,
},
{
title: "用户名称",
key: "memberName",
align: "left",
tooltip: true,
},
{
title: "申请金额",
key: "applyMoney",
align: "left",
width: 120,
render: (h, params) => {
return h("priceColorScheme", {props:{value:params.row.applyMoney,color:this.$mainColor}} );
},
},
{
title: "提现状态",
align: "left",
key: "applyStatus",
width: 120,
render: (h, params) => {
if (params.row.applyStatus == "APPLY") {
return h("Tag", { props: { color: "volcano" } }, "申请中");
} else if (params.row.applyStatus == "VIA_AUDITING") {
return h("Tag", { props: { color: "green" } }, "审核通过");
} else if (params.row.applyStatus == "WAIT_USER_CONFIRM") {
return h("Tag", { props: { color: "gold" } }, "用户确认");
} else if (params.row.applyStatus == "SUCCESS") {
return h("Tag", { props: { color: "blue" } }, "提现成功");
} else if (params.row.applyStatus == "ERROR") {
return h("Tag", { props: { color: "blue" } }, "提现失败");
} else {
return h("Tag", { props: { color: "red" } }, "审核拒绝");
}
}
},
{
title: "申请时间",
key: "createTime",
align: "left",
width: 170,
},
{
title: "审核时间",
key: "inspectTime",
align: "left",
width: 170,
},
{
title: "操作",
key: "action",
width: 120,
align: "center",
fixed: "right",
render: (h, params) => {
if (params.row.applyStatus == "APPLY") {
return h("div", { class: "ops" }, [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.showList = {};
this.roleModalVisible = true;
this.showList = params.row;
this.audit = "";
},
},
},
"审核"
),
]);
} else {
return h("div", { class: "ops" }, [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.showList = {};
this.queryModalVisible = true;
this.showList = params.row;
this.modalTitle = "查看";
},
},
},
"查看"
),
]);
}
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
selectDate: null,
submitLoading: false,
selectList: [],
selectCount: 0,
showList: {},
data: [],
total: 0,
};
},
filters: {
paramTypeFilter(val) {
if (val === "APPLY") {
return "申请中";
} else if (val === "VIA_AUDITING") {
return "审核通过(提现成功)";
} else if (val === "WAIT_USER_CONFIRM") {
return "用户确认";
} else if (val === "FAIL_AUDITING") {
return "审核拒绝";
} else if (val === "SUCCESS") {
return "提现成功";
} else if (val === "ERROR") {
return "提现失败";
} else {
return "未知状态";
}
},
},
methods: {
paramTypeFilter(val) {
if (val === "APPLY") return "申请中";
if (val === "VIA_AUDITING") return "审核通过(提现成功)";
if (val === "WAIT_USER_CONFIRM") return "用户确认";
if (val === "FAIL_AUDITING") return "审核拒绝";
if (val === "SUCCESS") return "提现成功";
if (val === "ERROR") return "提现失败";
return "未知状态";
},
applyStatusText(status) {
if (status === "APPLY") return "申请中";
if (status === "VIA_AUDITING") return "审核通过";
if (status === "WAIT_USER_CONFIRM") return "用户确认";
if (status === "SUCCESS") return "提现成功";
if (status === "ERROR") return "提现失败";
return "审核拒绝";
},
applyStatusTagType(status) {
if (status === "APPLY") return "warning";
if (status === "VIA_AUDITING") return "success";
if (status === "WAIT_USER_CONFIRM") return "warning";
if (status === "SUCCESS") return "primary";
if (status === "ERROR") return "primary";
return "danger";
},
openAudit(row) {
this.showList = { ...row };
this.audit = "";
this.roleModalVisible = true;
},
openQuery(row) {
this.showList = { ...row };
this.modalTitle = "查看";
this.queryModalVisible = true;
},
submitRole(res) {
const params = {};
params.applyId = this.showList.id;
@@ -319,33 +287,36 @@ export default {
this.searchForm.startDate = "";
this.searchForm.endDate = "";
this.searchForm.memberName = "";
// 重新加载数据
this.getDataList();
},
changeSort(e) {
this.searchForm.sort = e.key;
this.searchForm.order = e.order;
if (e.order === "normal") {
this.searchForm.sort = e.prop || e.key;
this.searchForm.order = e.order === "ascending" ? "asc" : e.order === "descending" ? "desc" : "";
if (!e.order) {
this.searchForm.order = "";
}
this.getDataList();
},
clearSelectAll() {
this.$refs.table.selectAll(false);
if (this.$refs.table) {
this.$refs.table.clearSelection();
}
},
changeSelect(e) {
this.selectList = e;
this.selectCount = e.length;
},
selectDateRange(v) {
if (v) {
if (v && v.length === 2) {
this.searchForm.startDate = v[0];
this.searchForm.endDate = v[1];
} else {
this.searchForm.startDate = "";
this.searchForm.endDate = "";
}
},
getDataList() {
this.loading = true;
// 带多条件搜索参数获取表单数据 请自行修改接口
getUserWithdrawApply(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
@@ -353,8 +324,6 @@ export default {
this.total = res.result.total;
}
});
this.total = this.data.length;
this.loading = false;
},
},
mounted() {
@@ -374,4 +343,3 @@ export default {
color: #dcdee2;
}
</style>

View File

@@ -1,42 +1,90 @@
<template>
<div class="search member-benefit">
<Card>
<Row class="operation padding-row">
<Button type="primary" @click="openAdd">添加权益设置</Button>
</Row>
<Table
:loading="loading"
<el-card>
<div class="operation padding-row">
<el-button type="primary" @click="openAdd">添加权益设置</el-button>
</div>
<el-table
v-loading="loading"
stripe
:columns="columns"
:data="data"
no-data-text="暂无数据"
empty-text="暂无数据"
class="mt_10 benefit-list-table benefit-list-table--no-vertical-borders"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
style="width: 100%"
>
<el-table-column label="权益类型" width="132">
<template #default="{ row }">
<span
v-if="row"
:title="benefitTypeLabel(row.benefitType)"
class="benefit-list-cell-ellipsis benefit-list-cell-ellipsis--type"
>
{{ benefitTypeLabel(row.benefitType) }}</span>
</template>
</el-table-column>
<el-table-column label="权益名称" width="188">
<template #default="{ row }">
<span
v-if="row"
:title="row.benefitName || '-'"
class="benefit-list-cell-ellipsis benefit-list-cell-ellipsis--name"
>
{{ row.benefitName || "-" }}</span>
</template>
</el-table-column>
<el-table-column label="权益LOGO" width="116" align="center">
<template #default="{ row }">
<span v-if="row && !row.benefitLogo">-</span>
<div v-else-if="row" class="benefit-logo-thumb benefit-logo-thumb--table">
<img :src="row.benefitLogo" alt="" />
</div>
</template>
</el-table-column>
<el-table-column label="状态" width="118" align="center">
<template #default="{ row }">
<el-switch
v-if="row"
:model-value="row.benefitState === 'OPEN'"
inline-prompt
active-text="开启"
inactive-text="关闭"
:loading="!!row._benefitStateLoading"
@change="(checked) => onBenefitStateSwitch(row, checked)"
/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="140">
<template #default="{ row }">
<div v-if="row" class="ops" style="display: flex; justify-content: center">
<a class="link-text" @click="openEdit(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</div>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 30, 50]"
:page-sizes="[20, 30, 50]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<!-- 平台优惠券选择添加优惠券活动coupon-publish 相同组件 -->
<Modal
<el-dialog
v-model="couponPickerVisible"
title="选择优惠券"
width="80%"
:z-index="3000"
:mask-closable="false"
@on-ok="handleCouponPickerClose"
@on-cancel="handleCouponPickerClose"
:close-on-click-modal="false"
destroy-on-close
@close="handleCouponPickerClose"
>
<couponTemplate
v-if="couponPickerVisible"
@@ -45,38 +93,41 @@
promotionStatus="START"
@selected="onCouponTemplateSelected"
/>
</Modal>
</el-dialog>
<Drawer
<el-drawer
v-model="addFlag"
title="添加权益设置"
width="1120"
placement="right"
size="1120px"
direction="rtl"
:z-index="950"
:mask-closable="false"
:closable="true"
scrollable
class-name="benefit-form-drawer"
:close-on-click-modal="false"
class="benefit-form-drawer"
>
<Form ref="addForm" :model="formAdd" :rules="rulesAdd" :label-width="110">
<FormItem label="权益类型" prop="benefitType">
<Select
<el-form ref="addForm" :model="formAdd" :rules="rulesAdd" label-width="110px">
<el-form-item label="权益类型" prop="benefitType">
<el-select
v-model="formAdd.benefitType"
clearable
placeholder="请选择权益类型"
style="width: 100%"
@on-change="onAddBenefitTypeChange"
@change="onAddBenefitTypeChange"
>
<Option v-for="item in benefitTypeOptions" :key="item.value" :value="item.value">{{ item.description }}</Option>
</Select>
</FormItem>
<FormItem
<el-option
v-for="item in benefitTypeOptions"
:key="item.value"
:value="item.value"
:label="item.description"
/>
</el-select>
</el-form-item>
<el-form-item
v-show="formAdd.benefitType === 'GIFT_POINT'"
label="赠送积分"
prop="giftPoint"
:required="formAdd.benefitType === 'GIFT_POINT'"
>
<InputNumber
<el-input-number
v-model="formAdd.giftPoint"
:min="0"
:max="99999"
@@ -84,28 +135,54 @@
style="width: 220px"
placeholder="必填,范围 0-99999"
/>
</FormItem>
<FormItem
</el-form-item>
<el-form-item
v-show="formAdd.benefitType === 'COUPON_PACKAGE'"
label="赠送优惠券"
prop="couponPackageRows"
:required="formAdd.benefitType === 'COUPON_PACKAGE'"
>
<div>
<Button type="dashed" icon="ios-add" class="mb_10" @click="openCouponPicker('add')">添加优惠券</Button>
<Table
<el-button plain class="mb_10" @click="openCouponPicker('add')">添加优惠券</el-button>
<el-table
border
size="small"
:columns="couponPackageTableColumns"
:data="formAdd.couponPackageRows"
no-data-text="请添加优惠券"
></Table>
empty-text="请添加优惠券"
style="width: 100%"
>
<el-table-column prop="couponName" label="优惠券名称" min-width="100" show-overflow-tooltip />
<el-table-column label="有效期" min-width="100">
<template #default="{ row }">
<span v-if="row" v-html="row.validRange || '-'" />
</template>
</el-table-column>
<el-table-column prop="faceValueLabel" label="面额" min-width="100" />
<el-table-column label="赠送张数" width="150">
<template #default="{ row, $index }">
<el-input-number
v-if="row"
:model-value="row.quantity"
:min="1"
:max="10"
:precision="0"
style="width: 100px"
@change="(val) => onCouponQuantityChange('add', $index, val)"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="90" align="center">
<template #default="{ row, $index }">
<a v-if="row" class="link-text" @click="removeCouponRow('add', $index)">删除</a>
</template>
</el-table-column>
</el-table>
</div>
</FormItem>
<FormItem label="权益名称" prop="benefitName">
<Input v-model="formAdd.benefitName" maxlength="50" placeholder="请输入权益名称" />
</FormItem>
<FormItem label="权益LOGO" prop="benefitLogo">
</el-form-item>
<el-form-item label="权益名称" prop="benefitName">
<el-input v-model="formAdd.benefitName" maxlength="50" placeholder="请输入权益名称" />
</el-form-item>
<el-form-item label="权益LOGO" prop="benefitLogo">
<div class="benefit-logo-field">
<div v-if="formAdd.benefitLogo" class="benefit-logo-thumb">
<img :src="formAdd.benefitLogo" alt="" />
@@ -114,57 +191,60 @@
<upload-pic-input v-model="formAdd.benefitLogo" placeholder="请上传权益 LOGO 图片地址" style="width: 100%" />
</div>
</div>
</FormItem>
<FormItem label="权益介绍" prop="benefitDesc">
<Input v-model="formAdd.benefitDesc" type="textarea" :rows="4" maxlength="500" placeholder="请输入权益介绍" />
</FormItem>
<FormItem label="启用状态" prop="benefitState">
<RadioGroup v-model="formAdd.benefitState">
<Radio label="OPEN">开启</Radio>
<Radio label="CLOSE">关闭</Radio>
</RadioGroup>
</FormItem>
<FormItem label="排序" prop="benefitSort">
<InputNumber v-model="formAdd.benefitSort" :min="1" :max="9999" :precision="0" style="width: 220px" />
</FormItem>
</Form>
</el-form-item>
<el-form-item label="权益介绍" prop="benefitDesc">
<el-input v-model="formAdd.benefitDesc" type="textarea" :rows="4" maxlength="500" placeholder="请输入权益介绍" />
</el-form-item>
<el-form-item label="启用状态" prop="benefitState">
<el-radio-group v-model="formAdd.benefitState">
<el-radio value="OPEN">开启</el-radio>
<el-radio value="CLOSE">关闭</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排序" prop="benefitSort">
<el-input-number v-model="formAdd.benefitSort" :min="1" :max="9999" :precision="0" style="width: 220px" />
</el-form-item>
</el-form>
<div class="benefit-drawer-footer-btns">
<Button @click="addFlag = false">取消</Button>
<Button type="primary" :loading="submitAddLoading" @click="submitAdd">提交</Button>
<el-button @click="addFlag = false">取消</el-button>
<el-button type="primary" :loading="submitAddLoading" @click="submitAdd">提交</el-button>
</div>
</Drawer>
</el-drawer>
<Drawer
<el-drawer
v-model="editFlag"
title="编辑权益设置"
width="1120"
placement="right"
size="1120px"
direction="rtl"
:z-index="950"
:mask-closable="false"
:closable="true"
scrollable
class-name="benefit-form-drawer"
:close-on-click-modal="false"
class="benefit-form-drawer"
>
<Form ref="editForm" :model="formEdit" :rules="rulesEdit" :label-width="110">
<Input v-model="formEdit.id" v-show="false" />
<FormItem label="权益类型" prop="benefitType">
<Select
<el-form ref="editForm" :model="formEdit" :rules="rulesEdit" label-width="110px">
<el-input v-model="formEdit.id" v-show="false" />
<el-form-item label="权益类型" prop="benefitType">
<el-select
v-model="formEdit.benefitType"
clearable
placeholder="请选择权益类型"
style="width: 100%"
@on-change="onEditBenefitTypeChange"
@change="onEditBenefitTypeChange"
>
<Option v-for="item in benefitTypeOptions" :key="item.value" :value="item.value">{{ item.description }}</Option>
</Select>
</FormItem>
<FormItem
<el-option
v-for="item in benefitTypeOptions"
:key="item.value"
:value="item.value"
:label="item.description"
/>
</el-select>
</el-form-item>
<el-form-item
v-show="formEdit.benefitType === 'GIFT_POINT'"
label="赠送积分"
prop="giftPoint"
:required="formEdit.benefitType === 'GIFT_POINT'"
>
<InputNumber
<el-input-number
v-model="formEdit.giftPoint"
:min="0"
:max="99999"
@@ -172,28 +252,54 @@
style="width: 220px"
placeholder="必填,范围 0-99999"
/>
</FormItem>
<FormItem
</el-form-item>
<el-form-item
v-show="formEdit.benefitType === 'COUPON_PACKAGE'"
label="赠送优惠券"
prop="couponPackageRows"
:required="formEdit.benefitType === 'COUPON_PACKAGE'"
>
<div>
<Button type="dashed" icon="ios-add" class="mb_10" @click="openCouponPicker('edit')">添加优惠券</Button>
<Table
<el-button plain class="mb_10" @click="openCouponPicker('edit')">添加优惠券</el-button>
<el-table
border
size="small"
:columns="couponPackageTableColumnsEdit"
:data="formEdit.couponPackageRows"
no-data-text="请添加优惠券"
></Table>
empty-text="请添加优惠券"
style="width: 100%"
>
<el-table-column prop="couponName" label="优惠券名称" min-width="100" show-overflow-tooltip />
<el-table-column label="有效期" min-width="100">
<template #default="{ row }">
<span v-if="row" v-html="row.validRange || '-'" />
</template>
</el-table-column>
<el-table-column prop="faceValueLabel" label="面额" min-width="100" />
<el-table-column label="赠送张数" width="150">
<template #default="{ row, $index }">
<el-input-number
v-if="row"
:model-value="row.quantity"
:min="1"
:max="10"
:precision="0"
style="width: 100px"
@change="(val) => onCouponQuantityChange('edit', $index, val)"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="90" align="center">
<template #default="{ row, $index }">
<a v-if="row" class="link-text" @click="removeCouponRow('edit', $index)">删除</a>
</template>
</el-table-column>
</el-table>
</div>
</FormItem>
<FormItem label="权益名称" prop="benefitName">
<Input v-model="formEdit.benefitName" maxlength="50" placeholder="请输入权益名称" />
</FormItem>
<FormItem label="权益LOGO" prop="benefitLogo">
</el-form-item>
<el-form-item label="权益名称" prop="benefitName">
<el-input v-model="formEdit.benefitName" maxlength="50" placeholder="请输入权益名称" />
</el-form-item>
<el-form-item label="权益LOGO" prop="benefitLogo">
<div class="benefit-logo-field">
<div v-if="formEdit.benefitLogo" class="benefit-logo-thumb">
<img :src="formEdit.benefitLogo" alt="" />
@@ -202,25 +308,25 @@
<upload-pic-input v-model="formEdit.benefitLogo" placeholder="请上传权益 LOGO 图片地址" style="width: 100%" />
</div>
</div>
</FormItem>
<FormItem label="权益介绍" prop="benefitDesc">
<Input v-model="formEdit.benefitDesc" type="textarea" :rows="4" maxlength="500" placeholder="请输入权益介绍" />
</FormItem>
<FormItem label="启用状态" prop="benefitState">
<RadioGroup v-model="formEdit.benefitState">
<Radio label="OPEN">开启</Radio>
<Radio label="CLOSE">关闭</Radio>
</RadioGroup>
</FormItem>
<FormItem label="排序" prop="benefitSort">
<InputNumber v-model="formEdit.benefitSort" :min="1" :max="9999" :precision="0" style="width: 220px" />
</FormItem>
</Form>
</el-form-item>
<el-form-item label="权益介绍" prop="benefitDesc">
<el-input v-model="formEdit.benefitDesc" type="textarea" :rows="4" maxlength="500" placeholder="请输入权益介绍" />
</el-form-item>
<el-form-item label="启用状态" prop="benefitState">
<el-radio-group v-model="formEdit.benefitState">
<el-radio value="OPEN">开启</el-radio>
<el-radio value="CLOSE">关闭</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排序" prop="benefitSort">
<el-input-number v-model="formEdit.benefitSort" :min="1" :max="9999" :precision="0" style="width: 220px" />
</el-form-item>
</el-form>
<div class="benefit-drawer-footer-btns">
<Button @click="editFlag = false">取消</Button>
<Button type="primary" :loading="submitEditLoading" @click="submitEdit">提交</Button>
<el-button @click="editFlag = false">取消</el-button>
<el-button type="primary" :loading="submitEditLoading" @click="submitEdit">提交</el-button>
</div>
</Drawer>
</el-drawer>
</div>
</template>
@@ -244,9 +350,7 @@ const buildDefaultForm = () => ({
benefitSort: 1,
benefitState: "OPEN",
benefitConfig: "",
/** 仅 UI赠送积分数量提交时写入 benefitConfig */
giftPoint: null,
/** 仅 UI券礼包已选优惠券含展示字段提交时写入 benefitConfig.coupons */
couponPackageRows: [],
});
@@ -265,109 +369,6 @@ export default {
pageNumber: 1,
pageSize: 20,
},
columns: [
{
title: "权益类型",
key: "benefitType",
width: 132,
render: (h, params) => {
const v = params.row.benefitType;
let text = "-";
if (v) {
const opt = this.benefitTypeOptions.find((o) => o.value === v);
text = opt ? opt.description : v;
}
return h(
"span",
{
attrs: { title: text },
class: "benefit-list-cell-ellipsis benefit-list-cell-ellipsis--type",
},
text
);
},
},
{
title: "权益名称",
key: "benefitName",
width: 188,
render: (h, params) => {
const text = params.row.benefitName || "-";
return h(
"span",
{
attrs: { title: text },
class: "benefit-list-cell-ellipsis benefit-list-cell-ellipsis--name",
},
text
);
},
},
{
title: "权益LOGO",
key: "benefitLogo",
width: 116,
align: "center",
render: (h, params) => {
const src = params.row.benefitLogo;
if (!src) return h("span", "-");
return h(
"div",
{
class: "benefit-logo-thumb benefit-logo-thumb--table",
},
[
h("img", {
attrs: { src, alt: "" },
}),
]
);
},
},
{
title: "状态",
key: "benefitState",
width: 118,
align: "center",
render: (h, params) => {
const row = params.row;
return h("i-switch", {
props: {
value: row.benefitState === "OPEN",
size: "large",
loading: !!row._benefitStateLoading,
},
on: {
"on-change": (checked) => {
this.onBenefitStateSwitch(row, checked);
},
},
}, [
h("span", { slot: "open" }, "开启"),
h("span", { slot: "close" }, "关闭"),
]);
},
},
{
title: "操作",
key: "action",
align: "center",
width: 140,
render: (h, params) => {
const linkStyle = {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
};
const sep = h("span", { style: { margin: "0 8px", color: "#dcdee2" } }, "|");
return h("div", { style: { display: "flex", justifyContent: "center" } }, [
h("a", { style: linkStyle, on: { click: () => this.openEdit(params.row) } }, "编辑"),
sep,
h("a", { style: linkStyle, on: { click: () => this.remove(params.row) } }, "删除"),
]);
},
},
],
data: [],
addFlag: false,
editFlag: false,
@@ -386,13 +387,6 @@ export default {
rulesEdit() {
return this.buildFormRules("edit");
},
couponPackageTableColumns() {
return this.buildCouponPackageTableColumns("add");
},
couponPackageTableColumnsEdit() {
return this.buildCouponPackageTableColumns("edit");
},
/** 传给优惠券列表组件,用于勾选回显(与 coupon-publish 一致用 id */
couponPickerSelectedList() {
const form = this.couponPickerWhich === "add" ? this.formAdd : this.formEdit;
return (form.couponPackageRows || []).map((r) => ({
@@ -402,6 +396,20 @@ export default {
},
},
methods: {
benefitTypeLabel(v) {
if (!v) return "-";
const opt = this.benefitTypeOptions.find((o) => o.value === v);
return opt ? opt.description : v;
},
onCouponQuantityChange(which, index, val) {
const formKey = which === "add" ? "formAdd" : "formEdit";
const refName = which === "add" ? "addForm" : "editForm";
this[formKey].couponPackageRows[index].quantity = val;
this.$nextTick(() => {
const ref = this.$refs[refName];
if (ref) ref.validateField("couponPackageRows");
});
},
buildFormRules(which) {
const formKey = which === "add" ? "formAdd" : "formEdit";
return {
@@ -478,66 +486,6 @@ export default {
validRange: this.formatCouponValidity(detail),
};
},
buildCouponPackageTableColumns(which) {
const formKey = which === "add" ? "formAdd" : "formEdit";
const linkStyle = { color: "#2d8cf0", cursor: "pointer" };
return [
{ title: "优惠券名称", key: "couponName", minWidth: 100, tooltip: true },
{
title: "有效期",
minWidth: 100,
render: (h, p) =>
h("span", {
domProps: { innerHTML: p.row.validRange || "-" },
}),
},
{ title: "面额", key: "faceValueLabel", minWidth: 100 },
{
title: "赠送张数",
key: "quantity",
width: 150,
render: (h, params) =>
h("InputNumber", {
props: {
min: 1,
max: 10,
precision: 0,
value: params.row.quantity,
},
style: { width: "100px" },
on: {
"on-change": (val) => {
this.$set(this[formKey].couponPackageRows[params.index], "quantity", val);
this.$nextTick(() => {
const ref = which === "add" ? this.$refs.addForm : this.$refs.editForm;
if (ref) ref.validateField("couponPackageRows");
});
},
},
}),
},
{
title: "操作",
key: "action",
width: 90,
align: "center",
render: (h, params) =>
h(
"a",
{
style: linkStyle,
on: {
click: () => {
this.removeCouponRow(which, params.index);
},
},
},
"删除"
),
},
];
},
/** 构造 benefitConfig JSON 字符串 */
buildBenefitConfigString(slice) {
const { benefitType, giftPoint, couponPackageRows } = slice;
if (benefitType === GIFT_POINT) {
@@ -552,7 +500,6 @@ export default {
}
return "";
},
/** 从详情 benefitConfig 解析赠送积分,解析失败时返回 null */
parseGiftPointFromConfig(benefitType, benefitConfigStr) {
if (benefitType !== GIFT_POINT || !benefitConfigStr) return null;
try {
@@ -613,7 +560,6 @@ export default {
}
return [];
},
/** 与添加时 formatCouponFace 一致;仅有 faceValueText 时去掉「减免」前缀 */
normalizeCouponFaceFromDetail(item) {
let face = this.formatCouponFace(item);
if (face !== "-") return face;
@@ -624,7 +570,6 @@ export default {
.replace(/^减免\s*/, "")
.trim() || "-";
},
/** 管理端详情 couponItems → 表格行(面额展示与添加一致) */
mapCouponItemsToPackageRows(items) {
if (!Array.isArray(items)) return [];
return items.map((item) => {
@@ -669,7 +614,7 @@ export default {
});
}
}
this.$set(this.formEdit, "couponPackageRows", rows);
this.formEdit.couponPackageRows = rows;
},
handleCouponPickerClose() {
this.couponPickerVisible = false;
@@ -678,7 +623,6 @@ export default {
this.couponPickerWhich = which;
this.couponPickerVisible = true;
},
/** 与 coupon-publish `selectedCoupon`:表格多选变更时同步到券礼包配置 */
onCouponTemplateSelected(selectedRows) {
const which = this.couponPickerWhich;
const form = which === "add" ? this.formAdd : this.formEdit;
@@ -689,7 +633,7 @@ export default {
const qty = existing ? existing.quantity : 1;
return this.buildCouponDisplayRow(row, qty);
});
this.$set(form, "couponPackageRows", rows);
form.couponPackageRows = rows;
this.$nextTick(() => {
const ref = this.$refs[refName];
if (ref) ref.validateField("couponPackageRows");
@@ -791,7 +735,6 @@ export default {
API_Member.getMemberBenefit(row.id).then((res) => {
if (res && res.success && res.result) {
const raw = res.result;
/** 新接口:{ benefit, couponItems };旧接口:扁平 MemberBenefit */
const detail = raw.benefit != null ? raw.benefit : raw;
const couponItems = raw.couponItems;
const bt = detail.benefitType || "";
@@ -866,19 +809,19 @@ export default {
title: "提示",
content: `<p>确定${text}该客户权益?</p>`,
onOk: () => {
this.$set(row, "_benefitStateLoading", true);
row._benefitStateLoading = true;
return API_Member.updateMemberBenefitState(row.id, nextState)
.then((res) => {
this.$set(row, "_benefitStateLoading", false);
row._benefitStateLoading = false;
if (res && res.success) {
this.$Message.success(`${text}成功`);
this.$set(row, "benefitState", nextState);
row.benefitState = nextState;
} else {
this.getData();
}
})
.catch(() => {
this.$set(row, "_benefitStateLoading", false);
row._benefitStateLoading = false;
});
},
});
@@ -913,17 +856,20 @@ export default {
border-top: 1px solid #e8eaec;
text-align: right;
}
.benefit-drawer-footer-btns .ivu-btn + .ivu-btn {
.benefit-drawer-footer-btns .el-button + .el-button {
margin-left: 8px;
}
.link-text {
color: #409eff;
cursor: pointer;
text-decoration: none;
}
.op-split {
margin: 0 8px;
color: #dcdfe6;
}
</style>
<!--
权益 LOGO 缩略图固定 100×100
不使用 .member-benefit 前缀Modal 默认 transfer body弹窗内节点不在 .member-benefit
Table render 生成的节点在子组件内同样用非 scoped
-->
<style lang="scss">
.benefit-logo-thumb {
width: 100px;
@@ -952,7 +898,6 @@ export default {
margin: 0 auto;
}
/* 权益列表:固定类型/名称列宽并省略过长文案(悬浮 title 可看全文) */
.member-benefit .benefit-list-table table {
table-layout: fixed;
}
@@ -971,9 +916,8 @@ export default {
max-width: 172px;
}
/* 列表不出现列与列之间的竖线(保留行间分隔) */
.member-benefit .benefit-list-table--no-vertical-borders .ivu-table th,
.member-benefit .benefit-list-table--no-vertical-borders .ivu-table td {
.member-benefit .benefit-list-table--no-vertical-borders .el-table th,
.member-benefit .benefit-list-table--no-vertical-borders .el-table td {
border-right: none !important;
}
</style>

View File

@@ -1,57 +1,80 @@
<template>
<div class="search">
<Card>
<Form
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
:label-width="90"
@keydown.enter.native="handleSearch"
@submit.native.prevent
label-width="90px"
class="search-form"
@keyup.enter="handleSearch"
>
<FormItem label="客户手机号" prop="mobile">
<Input
<el-form-item label="客户手机号" prop="mobile">
<el-input
v-model="searchForm.memberMobile"
clearable
placeholder="请输入客户手机号"
style="width: 220px"
/>
</FormItem>
<FormItem label="规则" prop="ruleKey">
<Select v-model="searchForm.ruleKey" clearable filterable style="width: 220px">
<Option v-for="item in ruleOptions" :key="item.value" :value="item.value">
{{ item.label }}
</Option>
</Select>
</FormItem>
<Button type="primary" icon="ios-search" @click="handleSearch">搜索</Button>
</Form>
</Card>
</el-form-item>
<el-form-item label="规则" prop="ruleKey">
<el-select v-model="searchForm.ruleKey" clearable filterable style="width: 220px">
<el-option
v-for="item in ruleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<Card>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
class="mt_10"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
<el-card>
<el-table v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
<el-table-column label="客户手机号" width="140" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row">{{ row.memberMobile || row.mobile || "-" }}</span>
</template>
</el-table-column>
<el-table-column label="规则名称" width="150" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row">{{ row.ruleName || findRuleName(row.ruleKey) || "-" }}</span>
</template>
</el-table-column>
<el-table-column label="变化经验值" width="120">
<template #default="{ row }">
<span v-if="row">{{ formatExperienceValue(row) }}</span>
</template>
</el-table-column>
<el-table-column label="经验值限额" width="120">
<template #default="{ row }">
<span v-if="row">{{ formatMaxValue(row) }}</span>
</template>
</el-table-column>
<el-table-column label="操作说明" min-width="200" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row">{{ row.content || row.remark || row.description || "-" }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="170" />
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</div>
</template>
@@ -86,67 +109,20 @@ export default {
memberMobile: "",
ruleKey: "",
},
columns: [
{
title: "客户手机号",
key: "memberMobile",
width: 140,
tooltip: true,
render: (h, params) => {
const v = params.row.memberMobile || params.row.mobile || "-";
return h("span", v);
},
},
{
title: "规则名称",
key: "ruleName",
width: 150,
tooltip: true,
render: (h, params) => {
const text = params.row.ruleName || this.findRuleName(params.row.ruleKey) || "-";
return h("span", text);
},
},
{
title: "变化经验值",
key: "value",
width: 120,
render: (h, params) => {
const v =
params.row.value ??
params.row.variableExperience ??
params.row.experience ??
params.row.variableValue;
return h("span", v == null ? "-" : v);
},
},
{
title: "经验值限额",
key: "maxValue",
width: 120,
render: (h, params) => {
const v = params.row.maxValue ?? params.row.maxExperience ?? params.row.limitValue;
return h("span", v == null ? "-" : v);
},
},
{
title: "操作说明",
key: "content",
minWidth: 200,
tooltip: true,
render: (h, params) => {
const text = params.row.content || params.row.remark || params.row.description || "-";
return h("span", text);
},
},
{ title: "创建时间", key: "createTime", width: 170 },
],
};
},
mounted() {
this.init();
},
methods: {
formatExperienceValue(row) {
const v = row.value ?? row.variableExperience ?? row.experience ?? row.variableValue;
return v == null ? "-" : v;
},
formatMaxValue(row) {
const v = row.maxValue ?? row.maxExperience ?? row.limitValue;
return v == null ? "-" : v;
},
findRuleName(ruleKey) {
const hit = this.ruleOptions.find((item) => item.value === ruleKey);
return hit ? hit.label : "";

View File

@@ -1,10 +1,226 @@
<template>
<div class="experience-setting">
<Card>
<Form :label-width="120" label-position="right">
<Table :loading="loading" :columns="columns" :data="form.items" class="mt_10 experience-table"></Table>
<FormItem label="经验值说明" style="margin-top: 16px" class="desc-item">
<Input
<el-card>
<el-form label-width="120px" label-position="right">
<el-table
v-loading="loading"
:data="form.items"
class="mt_10 experience-table"
style="width: 100%"
>
<el-table-column label="是否开启" width="90" align="center">
<template #default="{ row, $index }">
<el-checkbox
v-if="row"
:model-value="!!form.items[$index].enabled"
@change="(checked) => updateRuleEnabled($index, checked)"
/>
</template>
</el-table-column>
<el-table-column prop="ruleName" label="类型" width="160" />
<el-table-column label="经验值(1-100)" min-width="700">
<template #default="{ row, $index }">
<template v-if="row">
<div v-if="row.ruleKey === 'REGISTER'" class="rule-cell">
<div class="rule-row">
<span class="rule-label">获得经验值:</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">会员注册成功后可获得经验值</div>
</div>
<div v-else-if="row.ruleKey === 'SHARE'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<div class="rule-inline">
<span class="rule-label">分享商品详情页获得经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-inline">
<span class="rule-label">可获得经验值限额</span>
<el-input
:model-value="formatInputValue(form.items[$index].maxValue)"
style="width: 120px"
@input="(val) => updateRuleMaxValue($index, val)"
@blur="() => commitRuleMaxValue($index)"
/>
</div>
</div>
<div class="rule-tip">会员分享商城页面可获得的经验值</div>
</div>
<div v-else-if="row.ruleKey === 'COMMENT'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">对已购买商品完成提交评论获得经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">仅针对评论字数大于30字的评论进行发放</div>
</div>
<div v-else-if="row.ruleKey === 'FOLLOW_STORE'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<div class="rule-inline">
<span class="rule-label">获得经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-inline">
<span class="rule-label">可获得经验值限额</span>
<el-input
:model-value="formatInputValue(form.items[$index].maxValue)"
style="width: 120px"
@input="(val) => updateRuleMaxValue($index, val)"
@blur="() => commitRuleMaxValue($index)"
/>
</div>
</div>
<div class="rule-tip">关注店铺可获得经验值每个客户D相同店铺仅第一次关注可进行获得</div>
</div>
<div v-else-if="row.ruleKey === 'PROFILE'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">获得经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">完善个人基本信息可获得经验值每个会员仅可获得一次</div>
</div>
<div v-else-if="row.ruleKey === 'BIND_WECHAT'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">获取经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">绑定微信成功获得经验值每个会员仅可获得一次</div>
</div>
<div v-else-if="row.ruleKey === 'ADD_ADDRESS'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">获取经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">添加收货地址后获得经验值每个会员仅可获得一次</div>
</div>
<div v-else-if="row.ruleKey === 'SHARE_REGISTER'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<div class="rule-inline">
<span class="rule-label">获取的经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-inline">
<span class="rule-label">可获得经验值限额</span>
<el-input
:model-value="formatInputValue(form.items[$index].maxValue)"
style="width: 120px"
@input="(val) => updateRuleMaxValue($index, val)"
@blur="() => commitRuleMaxValue($index)"
/>
</div>
</div>
<div class="rule-tip">仅被注册成功后才可获得相应奖励经验值</div>
</div>
<div v-else-if="row.ruleKey === 'SHARE_BUY'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<div class="rule-inline">
<span class="rule-label">获取的经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-inline">
<span class="rule-label">可获得经验值限额</span>
<el-input
:model-value="formatInputValue(form.items[$index].maxValue)"
style="width: 120px"
@input="(val) => updateRuleMaxValue($index, val)"
@blur="() => commitRuleMaxValue($index)"
/>
</div>
</div>
<div class="rule-tip">仅被购买成功后才可获得相应奖励经验值</div>
</div>
<div v-else-if="row.ruleKey === 'SIGN_IN'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">获取的经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">客户每日签到后可获的经验值</div>
</div>
<div v-else-if="row.ruleKey === 'CONSUME'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">1元获取经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">客户消费1元可获取经验值向下取整</div>
</div>
<el-input
v-else
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</template>
</template>
</el-table-column>
</el-table>
<el-form-item label="经验值说明" style="margin-top: 16px" class="desc-item">
<el-input
v-model="form.description"
type="textarea"
:rows="4"
@@ -12,12 +228,12 @@
show-word-limit
placeholder="请输入经验值说明"
/>
</FormItem>
<FormItem>
<Button type="primary" :loading="submitLoading" @click="submit">保存</Button>
</FormItem>
</Form>
</Card>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitLoading" @click="submit">保存</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
@@ -58,340 +274,33 @@ export default {
loading: false,
submitLoading: false,
form: defaultForm(),
columns: [
{
title: "是否开启",
key: "enabled",
width: 90,
align: "center",
render: (h, params) => {
return h("Checkbox", {
props: { value: !!this.form.items[params.index].enabled },
on: {
"on-change": (checked) => {
this.updateRuleEnabled(params.index, checked);
},
},
});
},
},
{
title: "类型",
key: "ruleName",
width: 160,
},
{
title: "经验值(1-100)",
key: "value",
minWidth: 700,
render: (h, params) => {
const row = this.form.items[params.index] || {};
const inputNode = h("Input", {
style: { width: "120px" },
props: {
value: row.value == null ? "" : String(row.value),
number: true,
},
on: {
input: (val) => {
this.updateRuleValue(params.index, val);
},
"on-change": (v) => {
this.updateRuleValue(params.index, v);
},
"on-blur": () => {
this.commitRuleValue(params.index);
},
},
});
const maxInputNode = h("Input", {
style: { width: "120px" },
props: {
value: row.maxValue == null ? "" : String(row.maxValue),
number: true,
},
on: {
input: (val) => {
this.updateRuleMaxValue(params.index, val);
},
"on-change": (v) => {
this.updateRuleMaxValue(params.index, v);
},
"on-blur": () => {
this.commitRuleMaxValue(params.index);
},
},
});
if (params.row.ruleKey === "REGISTER") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center" } },
[h("span", { style: { marginRight: "8px" } }, "获得经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"会员注册成功后可获得经验值"
),
]
);
}
if (params.row.ruleKey === "SHARE") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", marginRight: "16px" } },
[h("span", { style: { marginRight: "8px" } }, "分享商品详情页获得经验值:"), inputNode]
),
h(
"div",
{ style: { display: "flex", alignItems: "center" } },
[h("span", { style: { marginRight: "8px" } }, "可获得经验值限额:"), maxInputNode]
),
]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"会员分享商城页面可获得的经验值"
),
]
);
}
if (params.row.ruleKey === "COMMENT") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "对已购买商品完成提交评论获得经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"仅针对评论字数大于30字的评论进行发放"
),
]
);
}
if (params.row.ruleKey === "FOLLOW_STORE") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", marginRight: "16px" } },
[h("span", { style: { marginRight: "8px" } }, "获得经验值:"), inputNode]
),
h(
"div",
{ style: { display: "flex", alignItems: "center" } },
[h("span", { style: { marginRight: "8px" } }, "可获得经验值限额:"), maxInputNode]
),
]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"关注店铺可获得经验值每个客户D相同店铺仅第一次关注可进行获得"
),
]
);
}
if (params.row.ruleKey === "PROFILE") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "获得经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"完善个人基本信息可获得经验值,每个会员仅可获得一次"
),
]
);
}
if (params.row.ruleKey === "BIND_WECHAT") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "获取经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"绑定微信成功获得经验值,每个会员仅可获得一次"
),
]
);
}
if (params.row.ruleKey === "ADD_ADDRESS") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "获取经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"添加收货地址后获得经验值,每个会员仅可获得一次"
),
]
);
}
if (params.row.ruleKey === "SHARE_REGISTER") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", marginRight: "16px" } },
[h("span", { style: { marginRight: "8px" } }, "获取的经验值:"), inputNode]
),
h(
"div",
{ style: { display: "flex", alignItems: "center" } },
[h("span", { style: { marginRight: "8px" } }, "可获得经验值限额:"), maxInputNode]
),
]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"仅被注册成功后才可获得相应奖励经验值"
),
]
);
}
if (params.row.ruleKey === "SHARE_BUY") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", marginRight: "16px" } },
[h("span", { style: { marginRight: "8px" } }, "获取的经验值:"), inputNode]
),
h(
"div",
{ style: { display: "flex", alignItems: "center" } },
[h("span", { style: { marginRight: "8px" } }, "可获得经验值限额:"), maxInputNode]
),
]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"仅被购买成功后才可获得相应奖励经验值"
),
]
);
}
if (params.row.ruleKey === "SIGN_IN") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "获取的经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"客户每日签到后可获的经验值"
),
]
);
}
if (params.row.ruleKey === "CONSUME") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "1元获取经验值"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"客户消费1元可获取经验值向下取整"
),
]
);
}
return inputNode;
},
},
],
};
},
mounted() {
this.loadData();
},
methods: {
formatInputValue(v) {
return v == null ? "" : String(v);
},
getRawInputValue(v) {
return v && v.target ? v.target.value : v;
},
updateRuleEnabled(index, enabled) {
const item = this.form.items[index] || {};
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
enabled: !!enabled,
});
};
},
updateRuleValue(index, v) {
const raw = this.getRawInputValue(v);
const next = Number(raw);
const item = this.form.items[index] || {};
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
value: Number.isFinite(next) ? next : null,
});
};
},
commitRuleValue(index) {
const item = this.form.items[index] || {};
@@ -402,41 +311,41 @@ export default {
else if (next > 100) value = 100;
else value = Math.floor(next);
}
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
value,
});
};
},
updateRuleMaxValue(index, v) {
const raw = this.getRawInputValue(v);
const item = this.form.items[index] || {};
if (raw == null || raw === "") {
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
maxValue: null,
});
};
return;
}
const next = Number(raw);
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
maxValue: Number.isFinite(next) ? next : null,
});
};
},
commitRuleMaxValue(index) {
const item = this.form.items[index] || {};
if (item.maxValue == null || item.maxValue === "") {
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
maxValue: null,
});
};
return;
}
const next = Number(item.maxValue);
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
maxValue: Number.isFinite(next) && next >= 1 ? Math.floor(next) : null,
});
};
},
normalizeConfig(val) {
if (!val) return {};
@@ -535,21 +444,52 @@ export default {
padding: 2px 0;
}
::v-deep .experience-table .ivu-table th {
.rule-cell {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.rule-row {
display: flex;
align-items: center;
}
.rule-row-wrap {
flex-wrap: wrap;
}
.rule-inline {
display: flex;
align-items: center;
margin-right: 16px;
}
.rule-label {
margin-right: 8px;
}
.rule-tip {
margin-top: 6px;
color: #808695;
font-size: 12px;
}
:deep(.experience-table .el-table__header th) {
background: #fafbfc;
}
::v-deep .experience-table .ivu-table td {
:deep(.experience-table .el-table__body td) {
padding-top: 12px;
padding-bottom: 12px;
}
::v-deep .experience-table .ivu-table-cell {
:deep(.experience-table .cell) {
font-size: 13px;
line-height: 1.7;
}
::v-deep .desc-item .ivu-form-item-label {
:deep(.desc-item .el-form-item__label) {
font-size: 16px;
font-weight: 600;
}

View File

@@ -1,120 +1,180 @@
<template>
<div class="search">
<Card>
<Row class="operation padding-row">
<Button type="primary" @click="openAdd">添加客户等级</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
class="mt_10"
></Table>
</Card>
<el-card>
<div class="operation padding-row">
<el-button type="primary" @click="openAdd">添加客户等级</el-button>
</div>
<Modal v-model="addFlag" title="添加客户等级" width="720" :z-index="950" :mask-closable="false">
<Form ref="addForm" :model="formAdd" :rules="rules" :label-width="110">
<FormItem label="等级名称" prop="gradeName">
<Input v-model="formAdd.gradeName" maxlength="50" placeholder="请输入等级名称" />
</FormItem>
<FormItem label="是否默认" prop="isDefault">
<RadioGroup v-model="formAdd.isDefault">
<Radio :label="true"></Radio>
<Radio :label="false"></Radio>
</RadioGroup>
</FormItem>
<FormItem label="等级图标" prop="gradeImage">
<upload-pic-input v-model="formAdd.gradeImage"></upload-pic-input>
</FormItem>
<FormItem label="等级背景图" prop="gradeBackground">
<upload-pic-input v-model="formAdd.gradeBackground"></upload-pic-input>
</FormItem>
<FormItem label="字体颜色" prop="gradeFontColor">
<Input v-model="formAdd.gradeFontColor" maxlength="20" placeholder="如:#333333" />
</FormItem>
<FormItem label="所需经验值" prop="requiredExperience">
<InputNumber v-model="formAdd.requiredExperience" :min="1" :precision="0" style="width: 220px"></InputNumber>
</FormItem>
<FormItem label="等级排序" prop="gradeSort">
<InputNumber v-model="formAdd.gradeSort" :min="1" :max="9999" :precision="0" style="width: 220px"></InputNumber>
</FormItem>
<FormItem label="等级开关" prop="gradeState">
<RadioGroup v-model="formAdd.gradeState">
<Radio label="OPEN">开启</Radio>
<Radio label="CLOSE">关闭</Radio>
</RadioGroup>
</FormItem>
<FormItem label="关联权益" prop="benefitIds">
<Select
:value="addBenefitOrder"
<el-table v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
<el-table-column prop="gradeName" label="等级名称" width="130" show-overflow-tooltip />
<el-table-column label="默认等级" width="95">
<template #default="{ row }">
<el-tag v-if="row" :type="row.isDefault === true ? 'success' : 'info'">
{{ row.isDefault === true ? "" : "" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="等级图标" width="100">
<template #default="{ row }">
<span v-if="row && !row.gradeImage">-</span>
<img
v-else-if="row"
:src="row.gradeImage"
alt="等级图标"
style="width: 48px; height: 48px; object-fit: contain; border: 1px solid #dcdee2; border-radius: 4px; background: #fff"
/>
</template>
</el-table-column>
<el-table-column label="等级背景图" width="120">
<template #default="{ row }">
<span v-if="row && !row.gradeBackground">-</span>
<img
v-else-if="row"
:src="row.gradeBackground"
alt="等级背景图"
style="width: 64px; height: 40px; object-fit: cover; border: 1px solid #dcdee2; border-radius: 4px; background: #fff"
/>
</template>
</el-table-column>
<el-table-column prop="requiredExperience" label="所需经验值" width="110" />
<el-table-column prop="gradeSort" label="等级排序" width="95" />
<el-table-column label="状态" width="118" align="center">
<template #default="{ row }">
<el-switch
v-if="row"
:model-value="row.gradeState === 'OPEN'"
inline-prompt
active-text="开启"
inactive-text="关闭"
:loading="!!row._gradeStateLoading"
@change="(checked) => onGradeStateSwitch(row, checked)"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="130" align="center">
<template #default="{ row }">
<div v-if="row" class="ops" style="display: flex; justify-content: center">
<a class="link-text" @click="openEdit(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</div>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="addFlag" title="添加客户等级" width="720px" :z-index="950" :close-on-click-modal="false" destroy-on-close>
<el-form ref="addForm" :model="formAdd" :rules="rules" label-width="110px">
<el-form-item label="等级名称" prop="gradeName">
<el-input v-model="formAdd.gradeName" maxlength="50" placeholder="请输入等级名称" />
</el-form-item>
<el-form-item label="是否默认" prop="isDefault">
<el-radio-group v-model="formAdd.isDefault">
<el-radio :value="true"></el-radio>
<el-radio :value="false"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="等级图标" prop="gradeImage">
<upload-pic-input v-model="formAdd.gradeImage" />
</el-form-item>
<el-form-item label="等级背景图" prop="gradeBackground">
<upload-pic-input v-model="formAdd.gradeBackground" />
</el-form-item>
<el-form-item label="字体颜色" prop="gradeFontColor">
<el-input v-model="formAdd.gradeFontColor" maxlength="20" placeholder="如:#333333" />
</el-form-item>
<el-form-item label="所需经验值" prop="requiredExperience">
<el-input-number v-model="formAdd.requiredExperience" :min="1" :precision="0" style="width: 220px" />
</el-form-item>
<el-form-item label="等级排序" prop="gradeSort">
<el-input-number v-model="formAdd.gradeSort" :min="1" :max="9999" :precision="0" style="width: 220px" />
</el-form-item>
<el-form-item label="等级开关" prop="gradeState">
<el-radio-group v-model="formAdd.gradeState">
<el-radio value="OPEN">开启</el-radio>
<el-radio value="CLOSE">关闭</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="关联权益" prop="benefitIds">
<el-select
:model-value="addBenefitOrder"
multiple
filterable
placeholder="请选择客户权益"
style="width: 100%"
@on-change="onAddBenefitIdsChange"
@change="onAddBenefitIdsChange"
>
<Option v-for="b in benefitOptions" :key="b.id" :value="String(b.id)">{{ benefitOptionLabel(b) }}</Option>
</Select>
</FormItem>
</Form>
<div slot="footer">
<Button @click="addFlag = false">取消</Button>
<Button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</Button>
</div>
</Modal>
<el-option
v-for="b in benefitOptions"
:key="b.id"
:value="String(b.id)"
:label="benefitOptionLabel(b)"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="addFlag = false">取消</el-button>
<el-button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</el-button>
</template>
</el-dialog>
<Modal v-model="editFlag" title="编辑客户等级" width="720" :z-index="950" :mask-closable="false">
<Form ref="editForm" :model="formEdit" :rules="rules" :label-width="110">
<Input v-model="formEdit.id" v-show="false" />
<FormItem label="等级名称" prop="gradeName">
<Input v-model="formEdit.gradeName" maxlength="50" placeholder="请输入等级名称" />
</FormItem>
<FormItem label="是否默认" prop="isDefault">
<RadioGroup v-model="formEdit.isDefault">
<Radio :label="true"></Radio>
<Radio :label="false"></Radio>
</RadioGroup>
</FormItem>
<FormItem label="等级图标" prop="gradeImage">
<upload-pic-input v-model="formEdit.gradeImage"></upload-pic-input>
</FormItem>
<FormItem label="等级背景图" prop="gradeBackground">
<upload-pic-input v-model="formEdit.gradeBackground"></upload-pic-input>
</FormItem>
<FormItem label="字体颜色" prop="gradeFontColor">
<Input v-model="formEdit.gradeFontColor" maxlength="20" placeholder="如:#333333" />
</FormItem>
<FormItem label="所需经验值" prop="requiredExperience">
<InputNumber v-model="formEdit.requiredExperience" :min="1" :precision="0" style="width: 220px"></InputNumber>
</FormItem>
<FormItem label="等级排序" prop="gradeSort">
<InputNumber v-model="formEdit.gradeSort" :min="1" :max="9999" :precision="0" style="width: 220px"></InputNumber>
</FormItem>
<FormItem label="等级开关" prop="gradeState">
<RadioGroup v-model="formEdit.gradeState">
<Radio label="OPEN">开启</Radio>
<Radio label="CLOSE">关闭</Radio>
</RadioGroup>
</FormItem>
<FormItem label="关联权益" prop="benefitIds">
<Select
:value="editBenefitOrder"
<el-dialog v-model="editFlag" title="编辑客户等级" width="720px" :z-index="950" :close-on-click-modal="false" destroy-on-close>
<el-form ref="editForm" :model="formEdit" :rules="rules" label-width="110px">
<el-input v-model="formEdit.id" style="display: none" />
<el-form-item label="等级名称" prop="gradeName">
<el-input v-model="formEdit.gradeName" maxlength="50" placeholder="请输入等级名称" />
</el-form-item>
<el-form-item label="是否默认" prop="isDefault">
<el-radio-group v-model="formEdit.isDefault">
<el-radio :value="true"></el-radio>
<el-radio :value="false"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="等级图标" prop="gradeImage">
<upload-pic-input v-model="formEdit.gradeImage" />
</el-form-item>
<el-form-item label="等级背景图" prop="gradeBackground">
<upload-pic-input v-model="formEdit.gradeBackground" />
</el-form-item>
<el-form-item label="字体颜色" prop="gradeFontColor">
<el-input v-model="formEdit.gradeFontColor" maxlength="20" placeholder="如:#333333" />
</el-form-item>
<el-form-item label="所需经验值" prop="requiredExperience">
<el-input-number v-model="formEdit.requiredExperience" :min="1" :precision="0" style="width: 220px" />
</el-form-item>
<el-form-item label="等级排序" prop="gradeSort">
<el-input-number v-model="formEdit.gradeSort" :min="1" :max="9999" :precision="0" style="width: 220px" />
</el-form-item>
<el-form-item label="等级开关" prop="gradeState">
<el-radio-group v-model="formEdit.gradeState">
<el-radio value="OPEN">开启</el-radio>
<el-radio value="CLOSE">关闭</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="关联权益" prop="benefitIds">
<el-select
:model-value="editBenefitOrder"
multiple
filterable
placeholder="请选择客户权益"
style="width: 100%"
@on-change="onEditBenefitIdsChange"
@change="onEditBenefitIdsChange"
>
<Option v-for="b in benefitOptions" :key="b.id" :value="String(b.id)">{{ benefitOptionLabel(b) }}</Option>
</Select>
</FormItem>
</Form>
<div slot="footer">
<Button @click="editFlag = false">取消</Button>
<Button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</Button>
</div>
</Modal>
<el-option
v-for="b in benefitOptions"
:key="b.id"
:value="String(b.id)"
:label="benefitOptionLabel(b)"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="editFlag = false">取消</el-button>
<el-button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -135,7 +195,6 @@ const buildDefaultForm = () => ({
benefitIds: "",
});
/** 权益多选:新勾选追加到末尾,取消勾选移除,保持已有顺序 */
function syncOrderedBenefitIds(prevOrder, selected) {
const sel = Array.isArray(selected) ? selected.map((id) => String(id)) : [];
const out = [];
@@ -157,144 +216,6 @@ export default {
data() {
return {
loading: true,
columns: [
{
title: "等级名称",
key: "gradeName",
width: 130,
tooltip: true,
},
{
title: "默认等级",
key: "isDefault",
width: 95,
render: (h, params) => {
const yes = params.row.isDefault === true;
return h(
"Tag",
{
props: {
color: yes ? "success" : "default",
},
},
yes ? "是" : "否"
);
},
},
{
title: "等级图标",
key: "gradeImage",
width: 100,
render: (h, params) => {
if (!params.row.gradeImage) return h("span", "-");
return h("img", {
attrs: {
src: params.row.gradeImage,
alt: "等级图标",
},
style: {
width: "48px",
height: "48px",
objectFit: "contain",
border: "1px solid #dcdee2",
borderRadius: "4px",
background: "#fff",
},
});
},
},
{
title: "等级背景图",
key: "gradeBackground",
width: 120,
render: (h, params) => {
if (!params.row.gradeBackground) return h("span", "-");
return h("img", {
attrs: {
src: params.row.gradeBackground,
alt: "等级背景图",
},
style: {
width: "64px",
height: "40px",
objectFit: "cover",
border: "1px solid #dcdee2",
borderRadius: "4px",
background: "#fff",
},
});
},
},
{
title: "所需经验值",
key: "requiredExperience",
width: 110,
},
{
title: "等级排序",
key: "gradeSort",
width: 95,
},
{
title: "状态",
key: "gradeState",
width: 118,
align: "center",
render: (h, params) => {
const row = params.row;
return h("i-switch", {
props: {
value: row.gradeState === "OPEN",
size: "large",
loading: !!row._gradeStateLoading,
},
on: {
"on-change": (checked) => {
this.onGradeStateSwitch(row, checked);
},
},
}, [
h("span", { slot: "open" }, "开启"),
h("span", { slot: "close" }, "关闭"),
]);
},
},
{
title: "操作",
key: "action",
align: "center",
width: 130,
render: (h, params) => {
const linkStyle = {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
};
const sep = h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
);
return h(
"div",
{ class: "ops", style: { display: "flex", justifyContent: "center" } },
[
h(
"a",
{ style: linkStyle, on: { click: () => this.openEdit(params.row) } },
"编辑"
),
sep,
h(
"a",
{ style: linkStyle, on: { click: () => this.remove(params.row) } },
"删除"
),
]
);
},
},
],
data: [],
addFlag: false,
editFlag: false,
@@ -302,7 +223,6 @@ export default {
submitEditLoading: false,
formAdd: buildDefaultForm(),
formEdit: buildDefaultForm(),
/** 关联权益 id 顺序(与 benefitIds 一致) */
addBenefitOrder: [],
editBenefitOrder: [],
benefitOptions: [],
@@ -506,19 +426,19 @@ export default {
title: "提示",
content: `<p>确定${text}该客户等级?</p>`,
onOk: () => {
this.$set(row, "_gradeStateLoading", true);
row._gradeStateLoading = true;
return API_Member.updateMemberGradeState(row.id, nextState)
.then((res) => {
this.$set(row, "_gradeStateLoading", false);
row._gradeStateLoading = false;
if (res && res.success) {
this.$Message.success(`${text}成功`);
this.$set(row, "gradeState", nextState);
row.gradeState = nextState;
} else {
this.getData();
}
})
.catch(() => {
this.$set(row, "_gradeStateLoading", false);
row._gradeStateLoading = false;
});
},
});
@@ -545,3 +465,15 @@ export default {
},
};
</script>
<style scoped>
.ops a {
color: #2d8cf0;
cursor: pointer;
text-decoration: none;
}
.ops span {
display: inline-block;
margin: 0 8px;
color: #dcdee2;
}
</style>

View File

@@ -1,61 +1,72 @@
<template>
<div class="search">
<Card>
<Row class="operation padding-row">
<Button type="primary" @click="openAdd">添加分组</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
class="mt_10"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
<el-card>
<div class="operation padding-row">
<el-button type="primary" @click="openAdd">添加分组</el-button>
</div>
</el-card>
<el-card>
<el-table ref="table" v-loading="loading" :data="data" border class="mt_10" style="width: 100%">
<el-table-column prop="groupName" label="分组名称" min-width="160" show-overflow-tooltip />
<el-table-column prop="description" label="分组描述" min-width="240" show-overflow-tooltip />
<el-table-column prop="createTime" label="创建时间" min-width="180" />
<el-table-column prop="updateTime" label="更新时间" min-width="180" />
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<div v-if="row" class="ops" style="display: flex; justify-content: center">
<a class="link-text" @click="openEdit(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</div>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
<Modal v-model="addFlag" title="添加分组">
<Form ref="addForm" :model="formAdd" :rules="rulesAdd" :label-width="90">
<FormItem label="分组名称" prop="groupName" style="width: 90%;">
<Input v-model="formAdd.groupName" maxlength="30" placeholder="请输入分组名称" />
</FormItem>
<FormItem label="分组描述" prop="description" style="width: 90%;">
<Input v-model="formAdd.description" maxlength="200" placeholder="请输入分组描述" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="addFlag = false">取消</Button>
<Button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</Button>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</Modal>
<Modal v-model="editFlag" title="编辑分组">
<Form ref="editForm" :model="formEdit" :rules="rulesEdit" :label-width="90">
<Input v-model="formEdit.id" v-show="false" />
<FormItem label="分组名称" prop="groupName" style="width: 90%;">
<Input v-model="formEdit.groupName" maxlength="30" placeholder="请输入分组名称" />
</FormItem>
<FormItem label="分组描述" prop="description" style="width: 90%;">
<Input v-model="formEdit.description" maxlength="200" placeholder="请输入分组描述" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="editFlag = false">取消</Button>
<Button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</Button>
</div>
</Modal>
</el-card>
<el-dialog v-model="addFlag" title="添加分组" width="500px" :close-on-click-modal="false" destroy-on-close>
<el-form ref="addForm" :model="formAdd" :rules="rulesAdd" label-width="90px">
<el-form-item label="分组名称" prop="groupName" style="width: 90%">
<el-input v-model="formAdd.groupName" maxlength="30" placeholder="请输入分组名称" />
</el-form-item>
<el-form-item label="分组描述" prop="description" style="width: 90%">
<el-input v-model="formAdd.description" maxlength="200" placeholder="请输入分组描述" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="addFlag = false">取消</el-button>
<el-button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</el-button>
</template>
</el-dialog>
<el-dialog v-model="editFlag" title="编辑分组" width="500px" :close-on-click-modal="false" destroy-on-close>
<el-form ref="editForm" :model="formEdit" :rules="rulesEdit" label-width="90px">
<el-input v-model="formEdit.id" style="display: none" />
<el-form-item label="分组名称" prop="groupName" style="width: 90%">
<el-input v-model="formEdit.groupName" maxlength="30" placeholder="请输入分组名称" />
</el-form-item>
<el-form-item label="分组描述" prop="description" style="width: 90%">
<el-input v-model="formEdit.description" maxlength="200" placeholder="请输入分组描述" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="editFlag = false">取消</el-button>
<el-button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -71,67 +82,6 @@ export default {
pageNumber: 1,
pageSize: 20,
},
columns: [
{
title: "分组名称",
key: "groupName",
minWidth: 160,
tooltip: true,
},
{
title: "分组描述",
key: "description",
minWidth: 240,
tooltip: true,
},
{
title: "创建时间",
key: "createTime",
width: 180,
},
{
title: "更新时间",
key: "updateTime",
width: 180,
},
{
title: "操作",
key: "action",
align: "center",
width: 200,
fixed: "right",
render: (h, params) => {
const linkStyle = {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
};
const sep = h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
);
const children = [
h(
"a",
{ style: linkStyle, on: { click: () => this.openEdit(params.row) } },
"编辑"
),
sep,
h(
"a",
{ style: linkStyle, on: { click: () => this.remove(params.row) } },
"删除"
),
];
return h(
"div",
{ class: "ops", style: { display: "flex", justifyContent: "center" } },
children
);
},
},
],
data: [],
total: 0,
addFlag: false,
@@ -148,10 +98,10 @@ export default {
description: "",
},
rulesAdd: {
groupName: [{ required: true, message: "请输入分组名称" }],
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
},
rulesEdit: {
groupName: [{ required: true, message: "请输入分组名称" }],
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
},
};
},
@@ -236,7 +186,7 @@ export default {
remove(row) {
this.$Modal.confirm({
title: "提示",
content: "<p>确定删除该分组?</p>",
content: "确定删除该分组?",
onOk: () => {
API_Member.deleteMemberGroup(row.id).then((res) => {
if (res && res.success) {
@@ -255,3 +205,16 @@ export default {
},
};
</script>
<style scoped>
.ops a {
color: #2d8cf0;
cursor: pointer;
text-decoration: none;
}
.ops span {
display: inline-block;
margin: 0 8px;
color: #dcdee2;
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,175 +1,173 @@
<template>
<div class="search">
<Row>
<Card>
<Row @keydown.enter.native="handleSearch">
<Form
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
:label-width="70"
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<Form-item label="会员名称" prop="username">
<Input
type="text"
<el-form-item label="会员名称" prop="username">
<el-input
v-model="searchForm.username"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="联系方式" prop="mobile">
<Input
type="text"
</el-form-item>
<el-form-item label="联系方式" prop="mobile">
<el-input
v-model="searchForm.mobile"
placeholder="请输入会员联系方式"
clearable
style="width: 240px"
/>
</Form-item>
<Button
@click="handleSearch"
class="search-btn"
type="primary"
icon="ios-search"
>搜索</Button
>
</Form>
</Row>
</Card>
<Card>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
class="mt_10"
sortable="custom"
>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
</Row>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 修改模态框 -->
<Modal
v-model="descFlag"
:title="descTitle"
@on-ok="handleSubmitModal"
width="500"
<el-card>
<el-table
ref="table"
v-loading="loading"
border
:data="data"
class="mt_10"
style="width: 100%"
>
<Form
ref="formValidate"
:model="formValidate"
:rules="ruleValidate"
:label-width="80"
>
<FormItem label="头像">
<el-table-column prop="username" label="会员名称" min-width="150" align="left" show-overflow-tooltip />
<el-table-column prop="nickName" label="昵称" min-width="120" align="left" show-overflow-tooltip />
<el-table-column label="联系方式" min-width="130">
<template #default="{ row }">
<span v-if="row">{{ row.mobile || "" }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="注册时间" min-width="180" />
<el-table-column label="积分数量" min-width="120" align="left">
<template #default="{ row }">
<span v-if="row">{{ row.point == void 0 ? "0" : row.point }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<div v-if="row" class="ops" style="display: flex; justify-content: center">
<template v-if="selectedMember">
<a class="link-text" @click="callback(row)">选择</a>
<span class="op-split">|</span>
</template>
<a class="link-text" @click="detail(row)">查看</a>
<template v-if="!selectedMember">
<span class="op-split">|</span>
<a class="link-text" @click="enable(row)">启用</a>
<span class="op-split">|</span>
<a class="link-text" @click="editPerm(row)">编辑</a>
</template>
</div>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<el-dialog v-model="descFlag" :title="descTitle" width="500px" destroy-on-close>
<el-form ref="formValidate" :model="formValidate" :rules="ruleValidate" label-width="80px">
<el-form-item label="头像">
<img :src="formValidate.face" class="face" />
<Button
type="text"
<el-button
type="primary"
link
class="upload"
@click="
() => {
this.picModelFlag = true;
this.$refs.ossManage.selectImage = true;
picModelFlag = true;
$refs.ossManage.selectImage = true;
}
"
>修改
</Button>
>修改</el-button>
<input type="file" style="display: none" id="file" />
</FormItem>
<FormItem label="会员名称" prop="name">
<Input
v-model="formValidate.username"
style="width: 200px"
disabled
/>
</FormItem>
<FormItem label="用户昵称" prop="name">
<Input v-model="formValidate.nickName" style="width: 200px" />
</FormItem>
<FormItem label="性别" prop="sex">
<RadioGroup
type="button"
button-style="solid"
v-model="formValidate.sex"
>
<Radio :label="1">
<span></span>
</Radio>
<Radio :label="0">
<span></span>
</Radio>
</RadioGroup>
</FormItem>
<FormItem label="修改密码" prop="password">
<Input
type="password"
style="width: 220px"
password
v-model="formValidate.newPassword"
/>
</FormItem>
<FormItem label="生日" prop="birthday">
<DatePicker
type="date"
format="yyyy-MM-dd"
</el-form-item>
<el-form-item label="会员名称" prop="name">
<el-input v-model="formValidate.username" style="width: 200px" disabled />
</el-form-item>
<el-form-item label="用户昵称" prop="name">
<el-input v-model="formValidate.nickName" style="width: 200px" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="formValidate.sex">
<el-radio-button :value="1"></el-radio-button>
<el-radio-button :value="0"></el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="修改密码" prop="password">
<el-input v-model="formValidate.newPassword" type="password" show-password style="width: 220px" />
</el-form-item>
<el-form-item label="生日" prop="birthday">
<el-date-picker
v-model="formValidate.birthday"
type="date"
value-format="YYYY-MM-DD"
style="width: 220px"
></DatePicker>
</FormItem>
<FormItem label="所在地" prop="mail">
{{ formValidate.region || '暂无地址' }}
<Button style="margin-left: 10px;" @click="$refs.map.open()">选择</Button>
</FormItem>
</Form>
</Modal>
<Modal width="1200px" v-model="picModelFlag">
<ossManage @callback="callbackSelected" :isComponent="true" :initialize="picModelFlag" ref="ossManage" />
</Modal>
/>
</el-form-item>
<el-form-item label="所在地" prop="mail">
{{ formValidate.region || "暂无地址" }}
<el-button style="margin-left: 10px" @click="$refs.map.open()">选择</el-button>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="descFlag = false">取消</el-button>
<el-button type="primary" @click="handleSubmitModal">确定</el-button>
</template>
</el-dialog>
<el-dialog v-model="picModelFlag" width="1200px">
<ossManage
ref="ossManage"
@callback="callbackSelected"
:isComponent="true"
:initialize="picModelFlag"
/>
</el-dialog>
<multipleMap ref="map" @callback="selectedRegion" />
</div>
</template>
<script>
import * as API_Member from "@/api/member.js";
import ossManage from "@/views/sys/oss-manage/ossManage";
import multipleMap from "@/components/map/multiple-map";
export default {
name: "memberRecycle",
components: {
ossManage,
multipleMap
multipleMap,
},
data() {
return {
selectedMember: false, //是否是其他组件调用
descTitle: "", // modal标题
descFlag: false, //编辑查看框
openSearch: true, // 显示搜索
loading: true, // 表单加载状态
selectedMember: false,
descTitle: "",
descFlag: false,
openSearch: true,
loading: true,
searchForm: {
// 请求参数
pageNumber: 1,
pageSize: 20,
order: "desc",
@@ -177,198 +175,72 @@ export default {
mobile: "",
disabled: "CLOSE",
},
picModelFlag: false, // 选择图片
formValidate: {}, // 表单数据
ruleValidate: {}, //修改验证
columns: [
{
title: "会员名称",
align: "left",
key: "username",
tooltip: true,
},
{
title: "昵称",
align: "left",
key: "nickName",
tooltip: true,
},
{
title: "联系方式",
width: 130,
key: "mobile",
render: (h, params) => {
if (params.row.mobile == null) {
return h("div", [h("span", {}, "")]);
} else {
return h("div", [h("span", {}, params.row.mobile)]);
}
},
},
{
title: "注册时间",
key: "createTime",
width: 180,
},
{
title: "积分数量",
align: "left",
width: 120,
render: (h, params) => {
return h(
"div",
{},
params.row.point == void 0 ? "0" : params.row.point
);
},
},
{
title: "操作",
key: "action",
align: "center",
width: 200,
fixed: "right",
render: (h, params) => {
const linkStyle = {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
};
const sep = h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
);
const children = [];
if (this.selectedMember) {
children.push(
h(
"a",
{ style: linkStyle, on: { click: () => this.callback(params.row) } },
"选择"
)
);
children.push(sep);
}
children.push(
h(
"a",
{ style: linkStyle, on: { click: () => this.detail(params.row) } },
"查看"
)
);
if (!this.selectedMember) {
children.push(sep);
children.push(
h(
"a",
{ style: linkStyle, on: { click: () => this.enable(params.row) } },
"启用"
)
);
children.push(sep);
children.push(
h(
"a",
{ style: linkStyle, on: { click: () => this.editPerm(params.row) } },
"编辑"
)
);
}
return h(
"div",
{ class: "ops", style: { display: "flex", justifyContent: "center" } },
children
);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
picModelFlag: false,
formValidate: {},
ruleValidate: {},
data: [],
total: 0,
};
},
methods: {
// 回调给父级
callback(val) {
this.$emit("callback", val);
},
// 初始化数据
init() {
this.getData();
},
// 分页 修改页码
changePage(v) {
this.searchForm.pageNumber = v;
changePage() {
this.getData();
},
// 分页 修改页数
changePageSize(v) {
changePageSize() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getData();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getData();
},
//查看详情修改
editPerm(val) {
this.descTitle = `查看用户 ${val.username}`;
this.descFlag = true;
this.getMemberInfo(val.id);
},
/**
* 查询查看会员详情
*/
getMemberInfo(id) {
API_Member.getMemberInfoData(id).then((res) => {
if (res.result) {
this.$set(this, "formValidate", res.result);
this.formValidate = res.result;
}
});
},
//查询会员列表
getData() {
this.loading = true;
API_Member.getMemberListData(this.searchForm).then((res) => {
if (res.success) {
this.loading = false;
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
});
},
// 选中的图片
callbackSelected(val) {
this.picModelFlag = false;
this.formValidate.face = val.url;
},
// 选中的地址
selectedRegion(val) {
if(val.type === 'select'){
const paths = val.data.map(item => item.name).join(',')
const ids = val.data.map(item => item.id).join(',')
this.$set(this.formValidate,'region',paths)
this.$set(this.formValidate,'regionId',ids)
}
else{
this.$set(this.formValidate,'region',val.data.addr)
this.$set(this.formValidate,'regionId',val.data.addrId)
if (val.type === "select") {
const paths = val.data.map((item) => item.name).join(",");
const ids = val.data.map((item) => item.id).join(",");
this.formValidate.region = paths;
this.formValidate.regionId = ids;
} else {
this.formValidate.region = val.data.addr;
this.formValidate.regionId = val.data.addrId;
}
},
//详细
detail(row) {
this.$options.filters.customRouterPush({ name: "member-detail", query: { id: row.id } })
this.$filters.customRouterPush({ name: "member-detail", query: { id: row.id } });
},
//启用会员
enable(v) {
let params = {
memberIds: [v.id],
@@ -382,26 +254,19 @@ export default {
if (res.success) {
this.$Message.success("启用成功");
this.getData();
} else {
// this.$Message.error(res.message);
}
});
},
});
},
// 提交修改数据
handleSubmitModal() {
const { nickName, sex, username, face, newPassword, id ,regionId,region} =
this.formValidate;
const { nickName, sex, username, face, newPassword, id, regionId, region } = this.formValidate;
let time = new Date(this.formValidate.birthday);
let birthday =
time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
let birthday = time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
let submit = {
regionId: regionId,
region: region,
nickName,
sex,
birthday,
face: face || "",
@@ -413,6 +278,7 @@ export default {
API_Member.updateMember(submit).then((res) => {
if (res.result) {
this.$Message.success("修改成功!");
this.descFlag = false;
this.init();
}
});

View File

@@ -1,98 +1,108 @@
<template>
<div>
<!--微信模板-->
<Modal v-model="wechatModal" width="530">
<p slot="header">
<Icon type="edit"></Icon>
<span>微信设置</span>
</p>
<div>
<Form ref="wechatFormData" :model="wechatFormData" label-position="left" :label-width="100">
<FormItem v-if="tab === 'WECHAT'" label="模板名称">
<Input v-model="wechatFormData.name" size="large" maxlength="9" disabled></Input>
</FormItem>
<FormItem v-if="tab === 'WECHAT'" label="头部信息" prop="first">
<Input v-model="wechatFormData.first" size="large" maxlength="50"></Input>
</FormItem>
<FormItem v-if="tab === 'WECHAT'" label="备注" prop="remark">
<Input class='textarea' :rows="5" :autosize="{maxRows:5,minRows: 5}" v-model="wechatFormData.remark"
type="textarea" maxlength="150"/>
</FormItem>
<FormItem label="是否开启" prop="enable">
<i-switch v-model="wechatFormData.enable" size="large">
<span slot="open">开启</span>
<span slot="close">关闭</span>
</i-switch>
</FormItem>
</Form>
<el-dialog v-model="wechatModal" title="微信设置" width="530px">
<el-form ref="wechatFormData" :model="wechatFormData" label-width="100px">
<el-form-item v-if="tab === 'WECHAT'" label="模板名称">
<el-input v-model="wechatFormData.name" maxlength="9" disabled />
</el-form-item>
<el-form-item v-if="tab === 'WECHAT'" label="头部信息" prop="first">
<el-input v-model="wechatFormData.first" maxlength="50" />
</el-form-item>
<el-form-item v-if="tab === 'WECHAT'" label="备注" prop="remark">
<el-input v-model="wechatFormData.remark" type="textarea" :rows="5" maxlength="150" />
</el-form-item>
<el-form-item label="是否开启" prop="enable">
<el-switch v-model="wechatFormData.enable" active-text="开启" inactive-text="关闭" />
</el-form-item>
</el-form>
<template #footer>
<el-button v-if="tab === 'WECHAT'" type="primary" @click="wechatFormDataEdit">保存</el-button>
<el-button v-else type="primary" @click="wechatMPFormDataEdit">保存</el-button>
</template>
</el-dialog>
</div>
<div slot="footer" style="text-align: right">
<Button v-if="tab === 'WECHAT'" type="primary" @click="wechatFormDataEdit">保存</Button>
<Button v-else type="primary" @click="wechatMPFormDataEdit">保存</Button>
</div>
</Modal>
<Card>
<Tabs @on-click="tabPaneChange" v-model="tab">
<TabPane label="微信消息" name="WECHAT">
<el-card>
<el-tabs v-model="tab" @tab-click="tabPaneChange">
<el-tab-pane label="微信消息" name="WECHAT">
<div class="search">
<Row class="operation mt_10">
<Button @click="weChatSync" type="primary">初始化微信消息</Button>
</Row>
<Table
:loading="loading"
border
:columns="weChatColumns"
:data="weChatData"
ref="weChatTable"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="weChatSearchForm.pageNumber"
<div class="operation mt_10">
<el-button type="primary" @click="weChatSync">初始化微信消息</el-button>
</div>
<el-table v-loading="loading" border :data="weChatData" ref="weChatTable" style="width: 100%">
<el-table-column prop="code" label="模板编号" width="500" sortable />
<el-table-column label="是否开启" width="150" sortable>
<template #default="{ row }">
<span v-if="row">{{ row.enable ? "开启" : "关闭" }}</span>
</template>
</el-table-column>
<el-table-column prop="name" label="模板名称" width="200" sortable />
<el-table-column prop="createTime" label="创建时间" sortable />
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<template v-if="row">
<a class="link-text" @click="wechatSettingAlert(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="delWeChat(row)">删除</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="weChatSearchForm.pageNumber"
v-model:page-size="weChatSearchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="weChatTotal"
:page-size="weChatSearchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next"
size="small"
></Page>
</Row>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</TabPane>
</div>
</el-tab-pane>
<TabPane label="微信小程序订阅消息" name="WECHATMP">
<el-tab-pane label="微信小程序订阅消息" name="WECHATMP">
<div class="search">
<Row class="operation mt_10">
<Button @click="weChatSync('mp')" type="primary">初始化微信小程序订阅消息</Button>
</Row>
<Table
:loading="loading"
border
:columns="weChatColumns"
:data="weChatMPData"
sortable="custom"
ref="weChatMPTable"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="weChatMPSearchForm.pageNumber"
<div class="operation mt_10">
<el-button type="primary" @click="weChatSync('mp')">初始化微信小程序订阅消息</el-button>
</div>
<el-table v-loading="loading" border :data="weChatMPData" ref="weChatMPTable" style="width: 100%">
<el-table-column prop="code" label="模板编号" width="500" sortable />
<el-table-column label="是否开启" width="150" sortable>
<template #default="{ row }">
<span v-if="row">{{ row.enable ? "开启" : "关闭" }}</span>
</template>
</el-table-column>
<el-table-column prop="name" label="模板名称" width="200" sortable />
<el-table-column prop="createTime" label="创建时间" sortable />
<el-table-column label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<template v-if="row">
<a class="link-text" @click="wechatSettingAlert(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="delWeChat(row)">删除</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="weChatMPSearchForm.pageNumber"
v-model:page-size="weChatMPSearchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="weChatMPTotal"
:page-size="weChatMPSearchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next"
size="small"
></Page>
</Row>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</TabPane>
</Tabs>
</Card>
</div>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
<script>
@@ -101,257 +111,147 @@ import {
getWechatMessagePage,
editWechatMessageTemplate,
delWechatMessageTemplate,
wechatMPMessageSync,
getWechatMPMessagePage,
editWechatMPMessageTemplate,
delWechatMPMessageTemplate
delWechatMPMessageTemplate,
} from "@/api/setting";
export default {
title: "wechat-message-manage",
data() {
return {
wechatModal: false,// modal展示
wechatFormData: {}, // 微信数据
wechatMPFormData: {}, // 微信订阅消息
tab: "WECHAT", // tab栏分类
searchForm: { // 请求参数
type: "WECHAT"
},
loading: true, // 表单加载状态
id: '', // 模板id
//微信消息查询
weChatSearchForm: {
// 搜索框对应data对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
},
weChatMPSearchForm: {
// 搜索框对应data对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
},
weChatColumns: [
{
title: "模板编号",
key: "code",
width: 500,
sortable: true
},
{
title: "是否开启",
key: "enable",
sortable: true,
width: 150,
render: (h, params) => {
if (params.row.enable == true) {
return h('div', [
h('span', {}, '开启'),
]);
} else {
return h('div', [
h('span', {}, '关闭'),
]);
}
},
},
{
title: "模板名称",
key: "name",
width: 200,
sortable: true
},
{
title: "创建时间",
key: "createTime",
sortable: true,
sortType: "desc",
},
{
title: "操作",
key: "action",
width: 200,
align: "center",
fixed: "right",
render: (h, params) => {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none"
},
on: {
click: () => {
this.wechatSettingAlert(params.row);
}
}
},
"编辑"
),
h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none"
},
on: {
click: () => {
this.delWeChat(params.row);
}
}
},
"删除"
)
]);
}
}
],
weChatData: [], // 表单数据
weChatMPData: [], // 表单数据
weChatTotal: 0, // 表单数据总数
weChatMPTotal: 0, // 表单数据总数
wechatModal: false,
wechatFormData: {},
wechatMPFormData: {},
tab: "WECHAT",
searchForm: { type: "WECHAT" },
loading: true,
id: "",
weChatSearchForm: { pageNumber: 1, pageSize: 20 },
weChatMPSearchForm: { pageNumber: 1, pageSize: 20 },
weChatData: [],
weChatMPData: [],
weChatTotal: 0,
weChatMPTotal: 0,
};
},
methods: {
// 初始化数据
init() {
this.getDataList();
},
changePage(v) {
this.searchForm.type = this.tab;
this.getDataList();
if (this.tab === "WECHAT") {
this.weChatSearchForm.pageNumber = v;
this.getWechatMessagePage();
} else {
this.weChatMPSearchForm.pageNumber = v;
this.getWechatMPMessagePage();
}
},
changePageSize(v) {
this.searchForm.type = this.tab;
this.getDataList();
if (this.tab === "WECHAT") {
this.weChatSearchForm.pageSize = v;
this.getWechatMessagePage();
} else {
this.weChatMPSearchForm.pageSize = v;
this.getWechatMPMessagePage();
}
},
//微信弹出框
wechatSettingAlert(v) {
this.wechatFormData = v
this.id = v.id
this.wechatModal = true
this.wechatFormData = v;
this.id = v.id;
this.wechatModal = true;
},
//同步微信消息
weChatSync(mp) {
this.$Modal.confirm({
title: "提示",
// 记得确认修改此处
content: "确认要初始化微信小程序消息订阅?",
loading: true,
onOk: () => {
// 同步微信消息模板
if (mp === "mp") {
wechatMPMessageSync().then(res => {
wechatMPMessageSync().then((res) => {
this.$Modal.remove();
if (res.success) {
this.$Message.success('微信小程序消息订阅初始化');
this.$Message.success("微信小程序消息订阅初始化");
this.getWechatMPMessagePage();
}
});
} else {
// 同步微信消息模板
wechatMessageSync().then(res => {
wechatMessageSync().then((res) => {
this.$Modal.remove();
if (res.success) {
this.$Message.success('微信消息模板初始化成功');
this.$Message.success("微信消息模板初始化成功");
this.getWechatMessagePage();
}
});
}
}
},
});
},
//微信设置保存
wechatFormDataEdit() {
this.$refs['wechatFormData'].validate((valid) => {
this.$refs.wechatFormData.validate((valid) => {
if (valid) {
if (!this.wechatFormData.updateTime) {
this.wechatFormData.updateTime = ''
this.wechatFormData.updateTime = "";
}
editWechatMessageTemplate(this.id, this.wechatFormData).then(res => {
if (res.message === 'success') {
this.$Message.success('微信模板修改成功');
editWechatMessageTemplate(this.id, this.wechatFormData).then((res) => {
if (res.message === "success") {
this.$Message.success("微信模板修改成功");
this.wechatModal = false;
this.getWechatMessagePage();
}
});
}
})
});
},
wechatMPFormDataEdit() {
this.$refs['wechatFormData'].validate((valid) => {
this.$refs.wechatFormData.validate((valid) => {
if (valid) {
editWechatMPMessageTemplate(this.id, this.wechatMPFormData).then(res => {
if (res.message === 'success') {
this.$Message.success('微信消息订阅模板修改成功');
editWechatMPMessageTemplate(this.id, this.wechatMPFormData).then((res) => {
if (res.message === "success") {
this.$Message.success("微信消息订阅模板修改成功");
this.wechatModal = false;
this.getWechatMessagePage();
this.getWechatMPMessagePage();
}
});
}
})
});
},
//删除微信模消息
delWeChat(v) {
this.$Modal.confirm({
title: "提示",
content: "确定删除此模板?",
loading: true,
onOk: () => {
// 删除微信消息模板
if (this.tab === "WECHAT") {
delWechatMessageTemplate(v.id).then(res => {
delWechatMessageTemplate(v.id).then((res) => {
if (res.success) {
this.$Modal.remove();
this.$Message.success('微信模板删除成功');
this.getWechatMessagePage()
this.$Message.success("微信模板删除成功");
this.getWechatMessagePage();
}
});
} else {
delWechatMPMessageTemplate(v.id).then(res => {
delWechatMPMessageTemplate(v.id).then((res) => {
if (res.success) {
this.$Modal.remove();
this.$Message.success('微信消息订阅删除成功');
this.getWechatMessagePage()
this.$Message.success("微信消息订阅删除成功");
this.getWechatMPMessagePage();
}
});
}
}
});
},
selectDateRange(v) {
if (v) {
this.searchForm.startDate = v[0];
this.searchForm.endDate = v[1];
}
});
},
getDataList() {
this.loading = true;
getWechatMessagePage(this.searchWe).then(res => {
this.loading = false;
if (res.success) {
this.weChatData = res.result.records;
this.weChatTotal = res.result.total;
}
});
this.getWechatMessagePage();
},
//分页获取微信消息
getWechatMessagePage() {
getWechatMessagePage(this.weChatSearchForm).then(res => {
this.loading = true;
getWechatMessagePage(this.weChatSearchForm).then((res) => {
this.loading = false;
if (res.success) {
this.weChatData = res.result.records;
@@ -359,9 +259,9 @@ export default {
}
});
},
//分页获取微信小程序消息订阅
getWechatMPMessagePage() {
getWechatMPMessagePage(this.weChatMPSearchForm).then(res => {
this.loading = true;
getWechatMPMessagePage(this.weChatMPSearchForm).then((res) => {
this.loading = false;
if (res.success) {
this.weChatMPData = res.result.records;
@@ -369,20 +269,30 @@ export default {
}
});
},
//tab切换事件
tabPaneChange(v) {
tabPaneChange(tab) {
const v = tab.paneName || this.tab;
this.searchForm.type = v;
//如果是微信消息则走单独的接口
if (v === "WECHAT") {
this.getWechatMessagePage();
} else if (v === "WECHATMP") {
this.getWechatMPMessagePage();
}
}
},
},
mounted() {
this.init();
}
},
};
</script>
<style scoped>
.link-text {
color: #2d8cf0;
cursor: pointer;
text-decoration: none;
}
.op-split {
display: inline-block;
margin: 0 8px;
color: #dcdee2;
}
</style>

View File

@@ -1,61 +1,60 @@
<template>
<div class="search">
<Card class="points-statistics-card">
<Row type="flex" justify="space-around" align="middle" class="points-statistics">
<Col :xs="24" :sm="12" class="points-statistics-item">
<el-card class="points-statistics-card">
<el-row type="flex" justify="space-around" align="middle" class="points-statistics">
<el-col :xs="24" :sm="12" class="points-statistics-item">
<div class="points-statistics-title">已发放积分数</div>
<div class="points-statistics-subtitle">历史累计发放积分数</div>
<div class="points-statistics-value">{{ formatNumber(pointsStatistics.totalPoint) }}</div>
</Col>
<Col :xs="24" :sm="12" class="points-statistics-item">
</el-col>
<el-col :xs="24" :sm="12" class="points-statistics-item">
<div class="points-statistics-title">未使用积分数</div>
<div class="points-statistics-subtitle">会员账户未使用积分数</div>
<div class="points-statistics-value">{{ formatNumber(pointsStatistics.unUsedPoint) }}</div>
</Col>
</Row>
</Card>
</el-col>
</el-row>
</el-card>
<div class="point-tabs-wrap">
<Tabs v-model="activeTab" class="point-tabs">
<TabPane label="积分列表" name="pointList">
<Card class="point-content-card">
<Form
@keydown.enter.native="handleMemberSearch"
<el-tabs v-model="activeTab" class="point-tabs">
<el-tab-pane label="积分列表" name="pointList">
<el-card class="point-content-card">
<el-form
ref="memberSearchForm"
:model="memberSearchForm"
inline
:label-width="70"
@submit.native.prevent
label-width="70px"
class="search-form"
@keyup.enter="handleMemberSearch"
>
<Form-item label="客户名称" prop="nickName">
<Input
<el-form-item label="客户名称" prop="nickName">
<el-input
v-model="memberSearchForm.nickName"
placeholder="请输入客户名称"
clearable
style="width: 180px"
/>
</Form-item>
<Form-item label="客户账号" prop="username">
<Input
</el-form-item>
<el-form-item label="客户账号" prop="username">
<el-input
v-model="memberSearchForm.username"
placeholder="请输入客户账号"
clearable
style="width: 180px"
/>
</Form-item>
<Form-item label="账号状态" prop="disabled">
<Select
</el-form-item>
<el-form-item label="账号状态" prop="disabled">
<el-select
v-model="memberSearchForm.disabled"
clearable
placeholder="请选择账号状态"
style="width: 160px"
>
<Option value="OPEN">启用</Option>
<Option value="CLOSE">禁用</Option>
</Select>
</Form-item>
<Form-item label="积分值">
<InputNumber
<el-option label="启用" value="OPEN" />
<el-option label="禁用" value="CLOSE" />
</el-select>
</el-form-item>
<el-form-item label="积分值">
<el-input-number
v-model="memberSearchForm.minPoint"
:min="0"
:precision="0"
@@ -63,113 +62,161 @@
style="width: 140px"
/>
<span class="point-range-separator">-</span>
<InputNumber
<el-input-number
v-model="memberSearchForm.maxPoint"
:min="0"
:precision="0"
placeholder="最大积分值"
style="width: 140px"
/>
</Form-item>
<Button @click="handleMemberSearch" class="search-btn" type="primary" icon="ios-search">搜索</Button>
</Form>
</Card>
<Card class="point-content-card member-list-card">
<div slot="title" class="card-title">用户列表</div>
<Table
:loading="memberLoading"
:columns="memberColumns"
</el-form-item>
<el-form-item>
<el-button class="search-btn" type="primary" @click="handleMemberSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="point-content-card member-list-card">
<template #header>
<div class="card-title">用户列表</div>
</template>
<el-table
v-loading="memberLoading"
:data="memberData"
class="member-table"
style="width: 100%"
>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="memberSearchForm.pageNumber"
<el-table-column label="客户名称" width="180" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row">{{ row.nickName || "-" }}</span>
</template>
</el-table-column>
<el-table-column label="客户账号" width="180" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row">{{ row.username || "-" }}</span>
</template>
</el-table-column>
<el-table-column label="账号状态" width="180">
<template #default="{ row }">
<el-tag v-if="row" :type="row.disabled === true ? 'success' : 'info'">
{{ row.disabled === true ? "启用" : "禁用" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="积分余额" width="180">
<template #default="{ row }">
<span v-if="row">{{ row.point == null ? 0 : row.point }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center">
<template #default="{ row }">
<a v-if="row" class="link-text" @click="detail(row)">详情</a>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="memberSearchForm.pageNumber"
v-model:page-size="memberSearchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="memberTotal"
:page-size="memberSearchForm.pageSize"
@on-change="changeMemberPage"
@on-page-size-change="changeMemberPageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
</TabPane>
<TabPane label="积分增减记录" name="pointChangeRecord">
<Card class="point-content-card">
<Form
@keydown.enter.native="handleSearch"
@current-change="changeMemberPage"
@size-change="changeMemberPageSize"
/>
</div>
</el-card>
</el-tab-pane>
<el-tab-pane label="积分增减记录" name="pointChangeRecord">
<el-card class="point-content-card">
<el-form
ref="searchForm"
:model="searchForm"
inline
:label-width="70"
@submit.native.prevent
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<Form-item label="会员名称" prop="username">
<Input
type="text"
<el-form-item label="会员名称" prop="username">
<el-input
v-model="searchForm.memberName"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</Form-item>
<Button @click="handleSearch" class="search-btn" type="primary" icon="ios-search">搜索</Button>
</Form>
</Card>
<Card class="point-content-card">
<Table
:loading="loading"
</el-form-item>
<el-form-item>
<el-button class="search-btn" type="primary" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="point-content-card">
<el-table
v-loading="loading"
border
:columns="columns"
:data="data"
ref="table"
class="mt_10 point-table"
style="width: 100%"
>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
<el-table-column prop="memberName" label="会员名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="content" label="操作内容" min-width="200" show-overflow-tooltip />
<el-table-column prop="beforePoint" label="之前积分" width="200" />
<el-table-column label="变动积分" width="200">
<template #default="{ row }">
<template v-if="row">
<priceColorScheme
v-if="row.pointType === 'INCREASE'"
:value="row.variablePoint"
color="green"
unit="+"
/>
<priceColorScheme
v-else
:value="row.variablePoint"
:color="$mainColor"
unit="-"
/>
</template>
</template>
</el-table-column>
<el-table-column prop="point" label="当前积分" width="200" />
<el-table-column prop="createTime" label="操作时间" width="200" />
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
</TabPane>
</Tabs>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
import * as API_Member from "@/api/member.js";
export default {
// 积分历史页面
name: "point",
data() {
return {
activeTab: "pointList",
loading: true, // 表单加载状态
loading: true,
memberLoading: false,
pointsStatistics: {
totalPoint: 0,
unUsedPoint: 0,
},
searchForm: { // 请求参数
searchForm: {
pageNumber: 1,
pageSize: 20,
},
@@ -182,136 +229,19 @@
minPoint: null,
maxPoint: null,
},
columns: [
{
title: "会员名称",
key: "memberName",
minWidth: 120,
tooltip: true
},
{
title: "操作内容",
key: "content",
minWidth: 200,
tooltip: true
},
{
title: "之前积分",
key: "beforePoint",
width: 200,
},
{
title: "变动积分",
key: "variablePoint",
width: 200,
render: (h, params) => {
if (params.row.pointType == 'INCREASE') {
return h("priceColorScheme", {props:{value:params.row.variablePoint,color:'green',unit:"+"}} );
} else {
return h("priceColorScheme", {props:{value:params.row.variablePoint,color:this.$mainColor,unit:"-"}} );
}
}
},
{
title: "当前积分",
key: "point",
width: 200,
},
{
title: "操作时间",
key: "createTime",
width: 200
},
],
memberColumns: [
{
title: "客户名称",
key: "nickName",
width: 180,
tooltip: true,
render: (h, params) => {
return h("span", params.row.nickName || "-");
},
},
{
title: "客户账号",
key: "username",
width: 180,
tooltip: true,
render: (h, params) => {
return h("span", params.row.username || "-");
},
},
{
title: "账号状态",
key: "disabled",
width: 180,
render: (h, params) => {
const enabled = params.row.disabled === true;
return h(
"Tag",
{
props: {
color: enabled ? "success" : "default",
},
},
enabled ? "启用" : "禁用"
);
},
},
{
title: "积分余额",
key: "point",
width: 180,
render: (h, params) => {
const point = params.row.point == null ? 0 : params.row.point;
return h("span", point);
},
},
{
title: "操作",
key: "action",
width: 100,
align: "center",
render: (h, params) => {
return h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.detail(params.row);
},
},
},
"详情"
);
},
},
],
data: [], // 表单数据
data: [],
memberData: [],
memberTotal: 0,
total: 0, // 表单数据总数
total: 0,
};
},
methods: {
// 回调给父级
callback(val) {
this.$emit("callback", val);
},
// 查看会员详情
detail(row) {
this.$options.filters.customRouterPush({ name: "member-detail", query: { id: row.id } });
this.$filters.customRouterPush({ name: "member-detail", query: { id: row.id } });
},
// 初始化数据
init() {
this.getStatistics();
this.getMemberList();
@@ -332,18 +262,15 @@
if (!Number.isFinite(numericValue)) return "0";
return numericValue.toLocaleString();
},
// 分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
this.getData();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getData();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
@@ -373,17 +300,18 @@
minPoint: this.memberSearchForm.minPoint,
maxPoint: this.memberSearchForm.maxPoint,
};
API_Member.getMemberListData(params).then((res) => {
API_Member.getMemberListData(params)
.then((res) => {
this.memberLoading = false;
if (res && res.success && res.result && res.result.records) {
this.memberData = res.result.records;
this.memberTotal = res.result.total;
}
}).catch(() => {
})
.catch(() => {
this.memberLoading = false;
});
},
//查新积分列表
getData() {
this.loading = true;
API_Member.getHistoryPointData(this.searchForm).then((res) => {
@@ -393,7 +321,6 @@
this.total = res.result.total;
}
});
this.loading = false;
},
},
mounted() {
@@ -414,24 +341,22 @@
}
.point-tabs {
::v-deep .ivu-tabs-bar {
:deep(.el-tabs__header) {
margin-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
}
::v-deep .ivu-tabs-nav .ivu-tabs-tab {
:deep(.el-tabs__item) {
padding: 8px 18px;
color: #515a6e;
background: #fff;
transition: all 0.2s ease;
}
::v-deep .ivu-tabs-nav .ivu-tabs-tab-active {
color: #2d8cf0;
:deep(.el-tabs__item.is-active) {
color: #409eff;
font-weight: 500;
}
::v-deep .ivu-tabs-ink-bar {
:deep(.el-tabs__active-bar) {
height: 2px;
border-radius: 2px;
}
@@ -467,23 +392,12 @@
margin-top: 8px;
}
.point-table {
::v-deep .ivu-table-border th,
::v-deep .ivu-table-border td {
border-right: 0;
}
}
.point-content-card {
margin-bottom: 12px;
::v-deep .ivu-card-body {
padding: 18px 20px;
}
}
.member-list-card {
::v-deep .ivu-card-head {
:deep(.el-card__header) {
border-bottom: 1px solid #f5f5f5;
}
}
@@ -495,13 +409,9 @@
}
.member-table {
::v-deep .ivu-table-header th {
:deep(.el-table__header th) {
background: #fafafa;
}
::v-deep .ivu-tag {
margin-right: 0;
}
}
.point-range-separator {
@@ -510,6 +420,12 @@
color: #808695;
}
.link-text {
color: #409eff;
cursor: pointer;
text-decoration: none;
}
.face {
width: 60px;
height: 60px;

View File

@@ -1,133 +1,70 @@
<template>
<div class="search">
<Card>
<Tabs value="RETURN_MONEY" @on-click="handleClickType">
<TabPane label="退款" name="RETURN_MONEY">
<Row class="operation" style="margin-bottom: 10px">
<Button @click="add" type="primary" >添加</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</TabPane>
<TabPane label="取消" name="CANCEL">
<Row class="operation" style="margin-bottom: 10px">
<Button @click="add" type="primary" icon="md-add">添加</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</TabPane>
<TabPane label="退货" name="RETURN_GOODS">
<Row class="operation" style="margin-bottom: 10px">
<Button @click="add" type="primary" icon="md-add">添加</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</TabPane>
<TabPane label="投诉" name="COMPLAIN">
<Row class="operation" style="margin-bottom: 10px">
<Button @click="add" type="primary" icon="md-add">添加</Button>
<Button @click="getDataList" icon="md-refresh">刷新</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</TabPane>
</Tabs>
</Card>
<Modal
:title="modalTitle"
v-model="modalVisible"
:mask-closable="false"
:width="500"
>
<Form ref="form" :model="form" :label-width="100" :rules="formValidate">
<FormItem label="售后原因" prop="reason">
<Input v-model="form.reason" maxlength="20" clearable style="width: 100%"/>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit"
>提交
</Button
>
<el-card>
<el-tabs v-model="activeTab" @tab-click="onTabClick">
<el-tab-pane label="退款" name="RETURN_MONEY">
<div class="operation" style="margin-bottom: 10px">
<el-button type="primary" @click="add">添加</el-button>
</div>
</Modal>
</el-tab-pane>
<el-tab-pane label="取消" name="CANCEL">
<div class="operation" style="margin-bottom: 10px">
<el-button type="primary" @click="add">添加</el-button>
</div>
</el-tab-pane>
<el-tab-pane label="退货" name="RETURN_GOODS">
<div class="operation" style="margin-bottom: 10px">
<el-button type="primary" @click="add">添加</el-button>
</div>
</el-tab-pane>
<el-tab-pane label="投诉" name="COMPLAIN">
<div class="operation" style="margin-bottom: 10px">
<el-button type="primary" @click="add">添加</el-button>
<el-button @click="getDataList">刷新</el-button>
</div>
</el-tab-pane>
</el-tabs>
<el-table v-loading="loading" border :data="data" ref="table" style="width: 100%">
<el-table-column prop="createBy" label="创建人" min-width="120" />
<el-table-column prop="reason" label="原因" min-width="400" />
<el-table-column prop="createTime" label="时间" min-width="100" />
<el-table-column label="操作" width="200" align="center">
<template #default="{ row }">
<template v-if="row">
<a class="link-text" @click="edit(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<el-dialog v-model="modalVisible" :title="modalTitle" width="500px" :close-on-click-modal="false">
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
<el-form-item label="售后原因" prop="reason">
<el-input v-model="form.reason" maxlength="20" clearable style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</div>
</template>
<script>
@@ -136,170 +73,72 @@
export default {
data() {
return {
modalVisible: false,//添加售后原因弹出框
modalTitle: "", //添加售后原因弹出框标题
loading: true, // 表单加载状态
submitLoading: false, // 添加或编辑提交状态
form: {
reason: ""
},//添加编辑表单
activeTab: "RETURN_MONEY",
modalVisible: false,
modalTitle: "",
loading: true,
submitLoading: false,
form: { reason: "" },
formValidate: {
reason: [
{
required: true,
message: "请输入售后原因",
trigger: "blur",
},
],
reason: [{ required: true, message: "请输入售后原因", trigger: "blur" }],
},
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
serviceType: "RETURN_MONEY"
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
serviceType: "RETURN_MONEY",
},
columns: [
{
title: "创建人",
key: "createBy",
minWidth: 120,
},
{
title: "原因",
key: "reason",
minWidth: 400,
},
{
title: "时间",
key: "createTime",
minWidth: 100,
},
{
title: "操作",
key: "action",
align: "center",
width: 200,
render: (h, params) => {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
marginRight: "5px"
},
on: {
click: () => {
this.edit(params.row);
}
}
},
"编辑"
),
h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none"
},
on: {
click: () => {
this.remove(params.row);
}
}
},
"删除"
)
]);
},
},
],
data: [], // 表单数据
total: 0,//条数
data: [],
total: 0,
};
},
methods: {
// 分页 修改页码
onTabClick(tab) {
this.handleClickType(tab.paneName);
},
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
this.clearSelectAll();
},
// 分页 修改页数
changePageSize(v) {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getDataList();
},
//切换tab
handleClickType(v) {
this.searchForm.pageNumber = 1 // 当前页数
this.searchForm.pageSize = 20 // 页面大小
//退款
if (v == "RETURN_MONEY") {
this.searchForm.serviceType = "RETURN_MONEY"
}
//退货
if (v == "RETURN_GOODS") {
this.searchForm.serviceType = "RETURN_GOODS"
}
//取消
if (v == "CANCEL") {
this.searchForm.serviceType = "CANCEL"
}
//取消
if (v == "COMPLAIN") {
this.searchForm.serviceType = "COMPLAIN"
}
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.searchForm.serviceType = v;
this.getDataList();
},
//获取售后原因数据
getDataList() {
this.loading = true;
API_Order.getAfterSaleReasonPage(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result.records;
this.total = res.result.total
this.total = res.result.total;
}
});
this.loading = false;
},
//添加售后原因
add() {
this.form.reason = ""
this.modalVisible = true
this.modalTitle = "添加售后原因"
this.form.reason = "";
this.modalVisible = true;
this.modalTitle = "添加售后原因";
},
//修改售后原因
edit(v) {
this.form.reason = v.reason
this.form.id = v.id
this.modalVisible = true
this.modalTitle = "修改售后原因"
this.form.reason = v.reason;
this.form.id = v.id;
this.modalVisible = true;
this.modalTitle = "修改售后原因";
},
//提交表单
handleSubmit() {
this.form.serviceType = this.searchForm.serviceType
this.form.serviceType = this.searchForm.serviceType;
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
if (this.modalTitle == '添加售后原因') {
// 添加
if (this.modalTitle == "添加售后原因") {
delete this.form.id;
API_Order.addAfterSaleReason(this.form).then((res) => {
this.submitLoading = false;
@@ -310,7 +149,6 @@
}
});
} else {
// 编辑
API_Order.editAfterSaleReason(this.form.id, this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
@@ -323,15 +161,12 @@
}
});
},
//删除售后原因
remove(v) {
this.$Modal.confirm({
title: "确认删除",
// 记得确认修改此处
content: "确认要删除此售后原因?",
loading: true,
onOk: () => {
// 删除
API_Order.delAfterSaleReason(v.id).then((res) => {
this.$Modal.remove();
if (res.success) {
@@ -341,11 +176,22 @@
});
},
});
}
},
},
mounted() {
this.getDataList();
},
};
</script>
<style scoped>
.link-text {
color: #2d8cf0;
cursor: pointer;
text-decoration: none;
}
.op-split {
display: inline-block;
margin: 0 8px;
color: #dcdee2;
}
</style>

Some files were not shown because too many files have changed in this diff Show More