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 = { module.exports = {
presets: [ presets: ["@vue/cli-plugin-babel/preset"],
[ };
'@vue/app',
{
useBuiltIns: 'entry'
}
]
]
}

View File

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

View File

@@ -5,7 +5,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name='description' content='在线购物平台'> <meta name='description' content='在线购物平台'>
<!-- <meta name="viewport" content="width=device-width,initial-scale=1.0"> --> <!-- <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> <title>admin</title>
<meta name="keywords" content="keywords" /> <meta name="keywords" content="keywords" />
<meta name="description" content="description" /> <meta name="description" content="description" />
@@ -72,7 +73,7 @@
<script src="<%=js%>"></script> <script src="<%=js%>"></script>
<% } %> <% } %>
<script src="/config.js"></script> <script src="/config.js"></script>
<script src="./tinymce/tinymce.min.js"></script> <script src="/tinymce/tinymce.min.js"></script>
<noscript> <noscript>
<strong <strong
>We're sorry but admin doesn't work properly without JavaScript >We're sorry but admin doesn't work properly without JavaScript

View File

@@ -1,32 +1,46 @@
<template> <template>
<div id="main" class="app-main"> <div id="main" class="app-main">
<router-view></router-view> <router-view />
</div> </div>
</template> </template>
<script> <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 { export default {
updated () { name: "App",
if (!localStorage.getItem('category') && this.$route.path !== '/login') { updated() {
getCategoryTree(0).then(res => { if (!localStorage.getItem("category") && this.$route.path !== "/login") {
getCategoryTree(0).then((res) => {
if (res.success) { 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> </script>
<style> <style>
html, html,
body { body {
margin: 0;
padding: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: #f0f0f0; background: #f0f0f0;
font-size: 12px; font-size: 14px;
} }
.app-main { .app-main {
@@ -42,14 +56,6 @@ body {
margin-right: 5px; margin-right: 5px;
} }
.ivu-btn-text:focus {
box-shadow: none !important;
}
.ivu-tag {
cursor: pointer;
}
.tox-notifications-container { .tox-notifications-container {
display: none !important; display: none !important;
} }

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
const plugins = [ 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 export default plugins

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,14 @@
<template> <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"> <template v-if="flag">
<goodsDialog <goodsDialog
@selected=" @selected="
@@ -21,7 +30,11 @@
class="linkDialog" class="linkDialog"
/> />
</template> </template>
</Modal> <template #footer>
<el-button @click="clickClose">取消</el-button>
<el-button type="primary" @click="clickOK">确定</el-button>
</template>
</el-dialog>
</template> </template>
<script> <script>
import goodsDialog from "./goods-dialog"; import goodsDialog from "./goods-dialog";
@@ -86,11 +99,7 @@ export default {
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
::v-deep .ivu-modal { :deep(.el-dialog__body) {
overflow: hidden;
height: 650px !important;
}
::v-deep .ivu-modal-body {
width: 100%; width: 100%;
height: 500px; height: 500px;
overflow: hidden; overflow: hidden;

View File

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

View File

@@ -42,12 +42,12 @@
} }
} }
::v-deep .ivu-scroll-container { :deep(.ivu-scroll-container){
width: 100% !important; width: 100% !important;
height: 400px !important; height: 400px !important;
} }
::v-deep .ivu-scroll-content { :deep(.ivu-scroll-content){
/* */ /* */
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -81,7 +81,7 @@
align-items: center; align-items: center;
margin: 10px; margin: 10px;
::v-deep img { :deep(img){
width: 60px; width: 60px;
height: 60px; height: 60px;
text-align: center; 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 { export default {
pages, pages: markRaw(pages),
marketing, marketing: markRaw(marketing),
shops, shops: markRaw(shops),
category, category: markRaw(category),
goods, goods: markRaw(goods),
other, other: markRaw(other),
special special: markRaw(special),
} };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,78 +1,34 @@
<template> <template>
<div> <div>
<Drawer width="300px" title="页面配置" v-model="drawer"> <el-drawer v-model="drawer" title="页面配置" size="300px">
<!-- 内容 --> <div class="config-empty">暂无可配置项</div>
<h3> </el-drawer>
内容设置
</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>
</div> </div>
</template> </template>
<script> <script>
import { mapState } from 'vuex'
export default { export default {
name: "configDrawer", name: "configDrawer",
data() { data() {
return { return {
drawer: false, 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: { methods: {
open() { open() {
this.drawer = true this.drawer = true;
}, },
close() { close() {
this.drawer = false this.drawer = false;
}, },
toggle() { },
this.drawer != this.drawer };
},
}
}
</script> </script>
<style lang="scss" scoped> <style scoped>
* { .config-empty {
color: #333 !important; color: #909399;
} text-align: center;
padding: 20px 0;
h3 {
margin: 10px 0 20px 0;
}
.config-item {
cursor: pointer;
margin-bottom: 20px;
justify-content: space-between;
} }
</style> </style>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,130 @@
import { getCurrentPermissionList } from "@/api/index"; import { getCurrentPermissionList } from "@/api/index";
import lazyLoading from "./lazyLoading.js"; import lazyLoading from "./lazyLoading.js";
import { router } from "@/router/index";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
let util = {}; 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) { util.title = function(title) {
title = title || "运营后台"; title = title || "运营后台";
window.document.title = title; window.document.title = title;
@@ -94,7 +214,7 @@ util.initRouter = function(vm) {
// 404路由需要和动态路由一起加载 // 404路由需要和动态路由一起加载
const otherRouter = [ const otherRouter = [
{ {
path: "/*", path: "/:pathMatch(.*)*",
name: "error-404", name: "error-404",
meta: { meta: {
title: "404-页面不存在" title: "404-页面不存在"
@@ -132,15 +252,11 @@ util.initRouter = function(vm) {
return; return;
} }
util.initAllMenuData(constRoutes, menuData); util.initAllMenuData(constRoutes, menuData);
util.initRouterNode(otherRoutes, otherRouter); util.registerDynamicRoutes(menuData);
// 添加所有主界面路由
vm.$store.commit( vm.$store.commit(
"updateAppRouter", "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); util.initMenuData(vm, menuData);
// 缓存数据 修改加载标识 // 缓存数据 修改加载标识
window.localStorage.setItem("menuData", JSON.stringify(menuData)); window.localStorage.setItem("menuData", JSON.stringify(menuData));
@@ -154,7 +270,7 @@ util.initRouter = function(vm) {
return; return;
} }
let menuData = JSON.parse(data); let menuData = JSON.parse(data);
// 添加菜单路由 util.registerDynamicRoutes(menuData);
util.initMenuData(vm, menuData); util.initMenuData(vm, menuData);
} }
}; };
@@ -235,17 +351,27 @@ util.initRouterNode = function(routers, data) {
// data为所有子菜单数据 // data为所有子菜单数据
for (let item of 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 (hasChildren) {
if (item.children && item.children.length > 0) {
menu.children = []; menu.children = [];
// Vue Router 4父子路由 name 必须不同,后端菜单常出现同名父子节点
if (menu.name) {
menu.name = `${menu.name}__layout`;
}
util.initRouterNode(menu.children, item.children); 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 = {}; const meta = {};
// 给页面添加标题、父级菜单name方便左侧菜单选中
meta.title = menu.title ? menu.title + " - 运营后台" : null; meta.title = menu.title ? menu.title + " - 运营后台" : null;
meta.firstRouterName = item.firstRouterName; meta.firstRouterName = item.firstRouterName;
menu.meta = meta; menu.meta = meta;

View File

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

View File

@@ -1,114 +1,88 @@
// The Vue build version to load with the `import` command import { createApp } from "vue";
// (runtime-only or standalone) has been set in webpack.base.conf with an alias. import "core-js/stable";
import Vue from 'vue' import "./styles/theme.less";
import ViewUI from 'view-design' import App from "./App.vue";
// import 'view-design/dist/styles/iview.css' import { router } from "./router/index";
import './styles/theme.less'; 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" const { aMapSecurityJsCode, mainColor } = require("@/config");
// import "regenerator-runtime/runtime"
import App from './App'
import {router} from './router/index'
import store from './store'
import i18n from '@/locale'
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) { if (aMapSecurityJsCode) {
window._AMapSecurityConfig = { window._AMapSecurityConfig = {
securityJsCode: aMapSecurityJsCode, securityJsCode: aMapSecurityJsCode,
}; };
} }
const PC_URL = BASE.PC_URL;
const WAP_URL = BASE.WAP_URL;
Vue.config.devtools = true; // 刷新深链(如 /member/user-manage前先从缓存注册动态路由
Vue.config.productionTip = false util.bootstrapDynamicRoutesFromCache();
const PC_URL = BASE.PC_URL; // 跳转买家端地址 pc端
const WAP_URL = BASE.WAP_URL; // 跳转买家端地址 wap端 const app = createApp(App);
Vue.prototype.linkTo = function (goodsId, skuId) {
// 跳转买家端商品 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; let src;
if (skuId) { if (skuId) {
src = `${PC_URL}/goodsDetail?skuId=${skuId}&goodsId=${goodsId}`; src = `${PC_URL}/goodsDetail?skuId=${skuId}&goodsId=${goodsId}`;
} else { } else {
src = `${PC_URL}/goodsDetail?goodsId=${goodsId}`; src = `${PC_URL}/goodsDetail?goodsId=${goodsId}`;
} }
window.open(src, "_blank"); window.open(src, "_blank");
}; };
Vue.prototype.wapLinkTo = function (goodsId, skuId) {
// app端二维码 app.config.globalProperties.wapLinkTo = function (goodsId, skuId) {
if (skuId) { if (skuId) {
return `${WAP_URL}/pages/product/goods?id=${skuId}&goodsId=${goodsId}`; 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} router.isReady().then(() => {
copyViewUi.Input.props.maxlength.default = inputMaxLength // 挂载最大输入值 app.mount("#app");
Vue.use(copyViewUi, {
i18n: (key, value) => i18n.t(key, value),
}); });
Vue.component('liliDialog', liliDialog) // 登录后由 App.vue 触发 initRouter 拉取/更新菜单
export { app, util };
// 挂载全局使用的方法
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');
}
})

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 { createRouter, createWebHistory } from "vue-router";
import ViewUI from 'view-design'; import NProgress from "nprogress";
import Util from '../libs/util'; import "nprogress/nprogress.css";
import VueRouter from 'vue-router'; import Util from "../libs/util";
import Cookies from 'js-cookie'; import Cookies from "js-cookie";
import {routers} from './router'; import store from "@/store";
import { routers } from "./router";
Vue.use(VueRouter); NProgress.configure({ showSpinner: false });
// 路由配置 export const router = createRouter({
const RouterConfig = { history: createWebHistory(),
mode: 'history', routes: routers,
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);
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
ViewUI.LoadingBar.start(); NProgress.start();
Util.title(to.meta.title); Util.title(to.meta.title);
next();
const name = to.name; const name = to.name;
const hasToken = Cookies.get("userInfoManager");
if (!Cookies.get('userInfoManager') && name !== 'login') { if (!hasToken && name !== "login") {
// 判断是否已经登录且前往的页面不是登录页 next({ name: "login" });
next({ return;
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") {
Util.title();
next({ name: "home_index" });
return;
}
if (hasToken) {
Util.toDefaultPage([...routers], name, router, next);
return;
}
next();
}); });
router.afterEach((to) => { router.afterEach((to) => {
Util.openNewPage(router.app, to.name, to.params, to.query); Util.openNewPage({ $store: store }, to.name, to.params, to.query);
ViewUI.LoadingBar.finish(); NProgress.done();
window.scrollTo(0, 0); window.scrollTo(0, 0);
}); });

View File

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

View File

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

View File

@@ -1,11 +1,12 @@
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import util from '@/libs/util';
const user = { const user = {
state: {}, state: {},
mutations: { mutations: {
logout () { logout () {
util.clearDynamicRoutes();
Cookies.remove('userInfoManager'); Cookies.remove('userInfoManager');
// 清空打开的页面等数据
localStorage.clear(); 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; border-radius: 0.4em;
flex-wrap: wrap; flex-wrap: wrap;
> .ivu-form-item { > .ivu-form-item,
> .el-form-item {
margin: 8px 10px !important; margin: 8px 10px !important;
} }
} }
@@ -42,7 +43,12 @@
overflow: hidden; overflow: hidden;
} }
// 为Card组件之间增加间距 // 列表页相邻 Card 间距Element Plus
.search > .el-card + .el-card {
margin-top: 10px;
}
// 兼容遗留 iView
.ivu-card + .ivu-card { .ivu-card + .ivu-card {
margin-top: 10px; margin-top: 10px;
} }

View File

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

View File

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

View File

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

View File

@@ -1,51 +1,66 @@
<template> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Row class="operation"> <div class="operation">
<Button @click="add" type="primary">添加</Button> <el-button type="primary" @click="add">添加</el-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
>
</div> </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> </div>
</template> </template>
@@ -54,118 +69,36 @@ import {
getCustomWordsPage, getCustomWordsPage,
delCustom, delCustom,
insertCustomWords, insertCustomWords,
updateCustomWords updateCustomWords,
} from "@/api/index"; } from "@/api/index";
import { regular } from "@/utils"; import { regular } from "@/utils";
export default { export default {
name: "custom-words", name: "custom-words",
data() { data() {
return { return {
loading: true, // 表单加载状态 loading: true,
modalType: 0, // 添加或编辑标识 modalType: 0,
modalVisible: false, // 添加或编辑显示 modalVisible: false,
modalTitle: "", // 添加或编辑标题 modalTitle: "",
searchForm: { searchForm: {
// 搜索框初始化对象 pageNumber: 1,
pageNumber: 1, // 当前页数 pageSize: 20,
pageSize: 20, // 页面大小 sort: "createTime",
sort: "createTime", // 默认排序字段 order: "desc",
order: "desc", // 默认排序方式
words: "", words: "",
}, },
form: { form: {
// 添加或编辑表单对象初始化数据
name: "", name: "",
}, },
// 表单验证规则
formValidate: { formValidate: {
name: [ name: [regular.REQUIRED, regular.VARCHAR20],
regular.REQUIRED,
regular.VARCHAR20
],
}, },
submitLoading: false, // 添加或编辑提交状态 submitLoading: false,
selectList: [], // 多选数据 selectList: [],
selectCount: 0, // 多选计数 selectCount: 0,
columns: [ data: [],
// 表头 total: 0,
{
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, // 表单数据总数
}; };
}, },
methods: { methods: {
@@ -187,17 +120,14 @@ export default {
this.getDataList(); this.getDataList();
}, },
clearSelectAll() { clearSelectAll() {
this.$refs.table.selectAll(false); this.$refs.table?.clearSelection();
}, },
// 选中状态变更
changeSelect(e) { changeSelect(e) {
this.selectList = e; this.selectList = e;
this.selectCount = e.length; this.selectCount = e.length;
}, },
// 获取列表数据
getDataList() { getDataList() {
this.loading = true; this.loading = true;
getCustomWordsPage(this.searchForm).then((res) => { getCustomWordsPage(this.searchForm).then((res) => {
this.loading = false; this.loading = false;
if (res.success) { if (res.success) {
@@ -208,12 +138,10 @@ export default {
this.total = this.data.length; this.total = this.data.length;
this.loading = false; this.loading = false;
}, },
// 提交数据
handleSubmit() { handleSubmit() {
this.$refs.form.validate((valid) => { this.$refs.form.validate((valid) => {
if (valid) { if (valid) {
this.submitLoading = true; this.submitLoading = true;
if (this.modalType == 0) { if (this.modalType == 0) {
delete this.form.id; delete this.form.id;
insertCustomWords(this.form).then((res) => { insertCustomWords(this.form).then((res) => {
@@ -226,7 +154,6 @@ export default {
}); });
} else { } else {
this.form.id = this.id; this.form.id = this.id;
// 编辑
updateCustomWords(this.form).then((res) => { updateCustomWords(this.form).then((res) => {
this.submitLoading = false; this.submitLoading = false;
if (res.success) { if (res.success) {
@@ -239,16 +166,13 @@ export default {
} }
}); });
}, },
// 添加
add() { add() {
this.modalType = 0; this.modalType = 0;
this.modalTitle = "添加"; this.modalTitle = "添加";
this.form = {} this.form = {};
this.$refs.form.resetFields(); this.$refs.form?.resetFields();
this.modalVisible = true; this.modalVisible = true;
}, },
// 修改
detail(v) { detail(v) {
this.modalType = 1; this.modalType = 1;
this.id = v.id; this.id = v.id;
@@ -256,15 +180,12 @@ export default {
this.modalVisible = true; this.modalVisible = true;
this.form.name = v.name; this.form.name = v.name;
}, },
// 删除
remove(v) { remove(v) {
this.$Modal.confirm({ this.$Modal.confirm({
title: "确认删除", title: "确认删除",
// 记得确认修改此处
content: "您确认要删除 " + v.name + " ?", content: "您确认要删除 " + v.name + " ?",
loading: true, loading: true,
onOk: () => { onOk: () => {
// 删除
delCustom(v.id).then((res) => { delCustom(v.id).then((res) => {
this.$Modal.remove(); this.$Modal.remove();
if (res.success) { if (res.success) {
@@ -275,7 +196,6 @@ export default {
}, },
}); });
}, },
// 批量删除
delAll() { delAll() {
if (this.selectCount <= 0) { if (this.selectCount <= 0) {
this.$Message.warning("您还未选择要删除的数据"); this.$Message.warning("您还未选择要删除的数据");
@@ -291,8 +211,7 @@ export default {
ids += e.id + ","; ids += e.id + ",";
}); });
ids = ids.substring(0, ids.length - 1); ids = ids.substring(0, ids.length - 1);
// 批量删除 delCustom(ids).then((res) => {
delSensitive(ids).then((res) => {
this.$Modal.remove(); this.$Modal.remove();
if (res.success) { if (res.success) {
this.$Message.success("操作成功"); this.$Message.success("操作成功");

View File

@@ -1,355 +1,198 @@
<template> <template>
<div> <div class="search">
<Card> <el-card>
<Row @keydown.enter.native.prevent="handleSearch"> <el-form
<Form ref="searchForm"
ref="searchForm" :model="searchForm"
:model="searchForm" inline
inline label-width="70px"
:label-width="70" class="search-form"
class="search-form" @keyup.enter="handleSearch"
> >
<Form-item label="会员名称" prop="memberName"> <el-form-item label="会员名称" prop="memberName">
<Input <el-input
type="text" v-model="searchForm.memberName"
v-model="searchForm.memberName" placeholder="请输入会员名称"
placeholder="请输入会员名称" clearable
clearable style="width: 240px"
style="width: 240px" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="searchForm.distributionStatus" style="width: 240px">
<el-option
v-for="item in distributionStatusList"
:key="item.value"
:value="item.value"
:label="item.label"
/> />
</Form-item> </el-select>
<Form-item label="状态"> </el-form-item>
<Select <el-form-item>
v-model="searchForm.distributionStatus" <el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
style="width: 240px" </el-form-item>
> </el-form>
<Option </el-card>
v-for="item in distributionStatusList"
:value="item.value" <el-card>
:key="item.value" <el-table
>{{ 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"
ref="table" ref="table"
v-loading="loading"
border
:data="data"
class="mt_10" class="mt_10"
></Table> style="width: 100%"
<Row type="flex" justify="end" class="mt_10"> >
<Page <el-table-column prop="memberName" label="会员名称" min-width="120" show-overflow-tooltip />
:current="searchForm.pageNumber" <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" :total="total"
:page-size="searchForm.pageSize" layout="total, sizes, prev, pager, next, jumper"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small" size="small"
show-total @current-change="changePage"
show-elevator @size-change="changePageSize"
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>
</div> </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> </div>
</template> </template>
<script> <script>
import {editDistribution, getDistributionListData, resumeDistribution, retreatDistribution} from "@/api/distribution"; import { editDistribution, getDistributionListData, resumeDistribution, retreatDistribution } from "@/api/distribution";
import {distributionStatusList} from "./dataJson.js"; import { distributionStatusList } from "./dataJson.js";
import {regular} from "@/utils"; import { regular } from "@/utils";
export default { export default {
name: "distribution", name: "distribution",
components: {},
data() { data() {
return { return {
distributionStatusList, // 分销状态 distributionStatusList,
loading: true, // 表单加载状态 loading: true,
modalVisible: false, // 添加或编辑显示 modalVisible: false,
distributionForm: { distributionForm: {
name: "", name: "",
idNumber: "", idNumber: "",
settlementBankAccountName: "", settlementBankAccountName: "",
settlementBankAccountNum: "", settlementBankAccountNum: "",
settlementBankBranchName: "" settlementBankBranchName: "",
}, },
// 表单验证规则
distributionFormValidate: { distributionFormValidate: {
name: [ name: [regular.REQUIRED, regular.VARCHAR20],
regular.REQUIRED, idNumber: [regular.REQUIRED, regular.VARCHAR20],
regular.VARCHAR20 settlementBankAccountName: [regular.REQUIRED, regular.VARCHAR20],
], settlementBankAccountNum: [regular.REQUIRED, regular.VARCHAR20],
idNumber: [ settlementBankBranchName: [regular.REQUIRED, regular.VARCHAR20],
regular.REQUIRED,
regular.VARCHAR20
],
settlementBankAccountName: [
regular.REQUIRED,
regular.VARCHAR20
],
settlementBankAccountNum: [
regular.REQUIRED,
regular.VARCHAR20
],
settlementBankBranchName: [
regular.REQUIRED,
regular.VARCHAR20
],
}, },
submitLoading: false, // 编辑提交状态 submitLoading: false,
searchForm: { searchForm: {
// 搜索框初始化对象 pageNumber: 1,
pageNumber: 1, // 当前页数 pageSize: 20,
pageSize: 20, // 页面大小
}, },
columns: [ data: [],
{ total: 0,
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, // 表单数据总数
}; };
}, },
methods: { methods: {
// 初始化数据
init() { init() {
this.getDataList(); this.getDataList();
}, },
// 分页 改变页码 changePage() {
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList(); this.getDataList();
}, },
// 分页 改变页数 changePageSize() {
changePageSize(v) { this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getDataList(); this.getDataList();
}, },
// 搜索
handleSearch() { handleSearch() {
this.searchForm.pageNumber = 1; this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20; this.searchForm.pageSize = 20;
this.getDataList(); this.getDataList();
}, },
// 获取列表数据
getDataList() { getDataList() {
this.loading = true; this.loading = true;
this.searchForm.status = "PASS"; this.searchForm.status = "PASS";
@@ -361,15 +204,12 @@ export default {
} }
}); });
}, },
// 清退分销商
retreat(v) { retreat(v) {
this.$Modal.confirm({ this.$Modal.confirm({
title: "提示", title: "提示",
// 记得确认修改此处
content: "您确认要清退 " + v.memberName + " ?", content: "您确认要清退 " + v.memberName + " ?",
loading: true, loading: true,
onOk: () => { onOk: () => {
// 删除
retreatDistribution(v.id).then((res) => { retreatDistribution(v.id).then((res) => {
this.$Modal.remove(); this.$Modal.remove();
if (res.success) { if (res.success) {
@@ -380,15 +220,12 @@ export default {
}, },
}); });
}, },
// 恢复分销商
resume(v) { resume(v) {
this.$Modal.confirm({ this.$Modal.confirm({
title: "提示", title: "提示",
// 记得确认修改此处
content: "您确认要恢复 " + v.memberName + " ?", content: "您确认要恢复 " + v.memberName + " ?",
loading: true, loading: true,
onOk: () => { onOk: () => {
// 删除
resumeDistribution(v.id).then((res) => { resumeDistribution(v.id).then((res) => {
this.$Modal.remove(); this.$Modal.remove();
if (res.success) { if (res.success) {
@@ -399,10 +236,8 @@ export default {
}, },
}); });
}, },
// 编辑
edit(v) { edit(v) {
this.$refs.distributionForm.resetFields(); this.$refs.distributionForm?.resetFields();
// 转换null为""
for (let attr in v) { for (let attr in v) {
if (v[attr] === null) { if (v[attr] === null) {
v[attr] = ""; v[attr] = "";
@@ -413,11 +248,9 @@ export default {
this.distributionForm = data; this.distributionForm = data;
this.modalVisible = true; this.modalVisible = true;
}, },
// 提交表单
handleSubmit() { handleSubmit() {
this.$refs.distributionForm.validate((valid) => { this.$refs.distributionForm.validate((valid) => {
if (valid) { if (valid) {
// 编辑
editDistribution(this.distributionForm).then((res) => { editDistribution(this.distributionForm).then((res) => {
this.submitLoading = false; this.submitLoading = false;
if (res.success) { if (res.success) {

View File

@@ -1,56 +1,68 @@
<template> <template>
<div> <div class="search">
<Card> <el-card>
<Row @keydown.enter.native.prevent="handleSearch"> <el-form
<Form ref="searchForm"
ref="searchForm" :model="searchForm"
:model="searchForm" inline
inline label-width="70px"
:label-width="70" class="search-form"
class="search-form" @keyup.enter="handleSearch"
> >
<Form-item label="会员名称" prop="memberName"> <el-form-item label="会员名称" prop="memberName">
<Input <el-input
type="text" v-model="searchForm.memberName"
v-model="searchForm.memberName" placeholder="请输入会员名称"
placeholder="请输入会员名称" clearable
clearable style="width: 240px"
style="width: 240px" />
/> </el-form-item>
</Form-item> <el-form-item>
<Button <el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
@click="handleSearch" </el-form-item>
type="primary" </el-form>
class="search-btn" </el-card>
>搜索</Button
> <el-card>
</Form> <el-table
</Row>
</Card>
<Card>
<Table
class="mt_10"
:loading="loading"
border
:columns="columns"
:data="data"
ref="table" ref="table"
></Table> v-loading="loading"
<Row type="flex" justify="end" class="mt_10"> border
<Page :data="data"
:current="searchForm.pageNumber" 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" :total="total"
:page-size="searchForm.pageSize" layout="total, sizes, prev, pager, next, jumper"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small" size="small"
show-total @current-change="changePage"
show-elevator @size-change="changePageSize"
show-sizer />
></Page> </div>
</Row> </el-card>
</Card>
</div> </div>
</template> </template>
@@ -61,115 +73,35 @@ export default {
name: "distributionApply", name: "distributionApply",
data() { data() {
return { return {
loading: true, // 表单加载状态 loading: true,
searchForm: { searchForm: {
// 搜索框初始化对象 pageNumber: 1,
pageNumber: 1, // 当前页数 pageSize: 20,
pageSize: 20, // 页面大小 sort: "createTime",
sort: "createTime", // 默认排序字段 order: "desc",
order: "desc", // 默认排序方式 startDate: "",
startDate: "", // 起始时间 endDate: "",
endDate: "", // 终止时间
}, },
columns: [ data: [],
{ total: 0,
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, // 表单数据总数
}; };
}, },
methods: { methods: {
// 初始化数据
init() { init() {
this.getDataList(); this.getDataList();
}, },
// 分页 改变页码 changePage() {
changePage(v) { this.getDataList();
this.searchForm.pageNumber = v; },
this.getDataList(); changePageSize() {
this.clearSelectAll(); this.searchForm.pageNumber = 1;
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
this.getDataList(); this.getDataList();
}, },
// 搜索
handleSearch() { handleSearch() {
this.searchForm.pageNumber = 1; this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20; this.searchForm.pageSize = 20;
this.getDataList(); this.getDataList();
}, },
// 获取列表数据
getDataList() { getDataList() {
this.loading = true; this.loading = true;
this.searchForm.distributionStatus = "APPLY"; this.searchForm.distributionStatus = "APPLY";
@@ -180,10 +112,7 @@ export default {
this.total = res.result.total; this.total = res.result.total;
} }
}); });
this.total = this.data.length;
this.loading = false;
}, },
//审核
audit(v, status) { audit(v, status) {
let test = "拒绝"; let test = "拒绝";
if (status == "PASS") { if (status == "PASS") {
@@ -194,7 +123,6 @@ export default {
}; };
this.$Modal.confirm({ this.$Modal.confirm({
title: "确认" + test, title: "确认" + test,
// 记得确认修改此处
content: "您确认要" + test + " " + v.memberName + " ?", content: "您确认要" + test + " " + v.memberName + " ?",
loading: true, loading: true,
onOk: () => { onOk: () => {

View File

@@ -1,270 +1,219 @@
<template> <template>
<div> <div class="search">
<Card> <el-card>
<Form ref="searchForm" @keydown.enter.native.prevent="handleSearch" :model="searchForm" class="search-form"> <el-form
<Form-item label="会员名称" class="flex" prop="memberName"> ref="searchForm"
<Input :model="searchForm"
type="text" placeholder="请输入会员名称" v-model="searchForm.memberName" clearable class="search-form"
style="width: 240px"></Input> @keyup.enter="handleSearch"
</Form-item> >
<Form-item label="编号" class="flex"> <el-form-item label="会员名称" class="flex" prop="memberName">
<Input <el-input
type="text" placeholder="请输入编号" v-model="searchForm.sn" clearable v-model="searchForm.memberName"
style="width: 240px"></Input> placeholder="请输入会员名称"
</Form-item> clearable
<Form-item label="状态" style="width: 240px"
style="width: 240px"> />
<Select v-model="searchForm.distributionCashStatus" clearable style="width: 150px"> </el-form-item>
<Option v-for="item in cashStatusList" :value="item.value" :key="item.value">{{ item.label }}</Option> <el-form-item label="编号" class="flex">
</Select> <el-input
</Form-item> v-model="searchForm.sn"
<Form-item> placeholder="请输入编号"
<Button @click="handleSearch" type="primary">搜索</Button> clearable
</Form-item> style="width: 240px"
</Form> />
</Card> </el-form-item>
<Card> <el-form-item label="状态" style="width: 240px">
<Table :loading="loading" border :columns="columns" :data="data" ref="table" class="mt_10"></Table> <el-select v-model="searchForm.distributionCashStatus" clearable style="width: 150px">
<Row type="flex" justify="end" class="page padding-row"> <el-option
<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> v-for="item in cashStatusList"
</Row> :key="item.value"
</Card> :value="item.value"
<Modal :title="modalTitle" v-model="modalVisible" :mask-closable='false' :width="500"> :label="item.label"
<Form ref="form" :model="form" :label-width="100" > />
<FormItem label="编号"> </el-select>
<Input disabled v-model="form.sn" clearable style="width:100%"/> </el-form-item>
</FormItem> <el-form-item>
<FormItem label="会员名称"> <el-button type="primary" @click="handleSearch">搜索</el-button>
<Input disabled v-model="form.distributionName" clearable style="width:100%"/> </el-form-item>
</FormItem> </el-form>
<FormItem label="金额"> </el-card>
<Input disabled v-model="form.price" clearable style="width:100%"/>
</FormItem> <el-card>
<FormItem label="是否通过" prop="result" v-if="handleStatus =='edit'"> <el-table
<RadioGroup v-model="result" type="button" button-style="solid"> ref="table"
<Radio label="VIA_AUDITING">通过</Radio> v-loading="loading"
<Radio label="FAIL_AUDITING">拒绝</Radio> border
</RadioGroup> :data="data"
</FormItem> class="mt_10"
</Form> style="width: 100%"
<div slot="footer" v-if="handleStatus == 'edit'"> >
<Button type="text" @click="modalVisible=false">取消</Button> <el-table-column prop="sn" label="编号" min-width="200" />
<Button type="primary" :loading="submitLoading" @click="handleSubmit">提交</Button> <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>
</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> </div>
</Modal> </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> </div>
</template> </template>
<script> <script>
import { import { auditDistributionCash, getDistributionCash } from "@/api/distribution";
auditDistributionCash, import { cashStatusList } from "./dataJson";
getDistributionCash
} from "@/api/distribution";
import { cashStatusList } from './dataJson';
export default { export default {
name: "distributionCash", name: "distributionCash",
data() { data() {
return { return {
cashStatusList, // 状态列表 cashStatusList,
loading: true, // 表单加载状态 loading: true,
modalVisible: false, // 添加或编辑显示 modalVisible: false,
modalTitle: "", // 添加或编辑标题 modalTitle: "",
result: 'FAIL_AUDITING', // 是否通过 result: "FAIL_AUDITING",
searchForm: { // 搜索框初始化对象 searchForm: {
pageNumber: 1, // 当前页数 pageNumber: 1,
pageSize: 20, // 页面大小 pageSize: 20,
sort: "createTime", // 默认排序字段 sort: "createTime",
order: "desc", // 默认排序方式 order: "desc",
}, },
handleStatus:'edit',// 判断是编辑还是查看 handleStatus: "edit",
form: { // 添加或编辑表单对象初始化数据 form: {
sn: "", sn: "",
memberName: "", memberName: "",
price: "", price: "",
}, },
submitLoading: false, // 添加或编辑提交状态 submitLoading: false,
columns: [ data: [],
{ total: 0,
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 // 表单数据总数
}; };
}, },
methods: { methods: {
// 初始化数据
init() { init() {
this.getDataList(); this.getDataList();
}, },
// 改变页码 changePage() {
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList(); this.getDataList();
}, },
// 改变页数 changePageSize() {
changePageSize(v) { this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getDataList(); this.getDataList();
}, },
// 搜索
handleSearch() { handleSearch() {
this.searchForm.pageNumber = 1; this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20; this.searchForm.pageSize = 20;
this.getDataList(); this.getDataList();
}, },
// 获取列表数据
getDataList() { getDataList() {
this.loading = true; this.loading = true;
// 带多条件搜索参数获取表单数据 请自行修改接口 getDistributionCash(this.searchForm).then((res) => {
getDistributionCash(this.searchForm).then(res => {
this.loading = false; this.loading = false;
if (res.success) { if (res.success) {
this.data = res.result.records; this.data = res.result.records;
this.total = res.result.total; this.total = res.result.total;
} }
}); });
this.total = this.data.length;
this.loading = false;
}, },
// 通过还是拒绝申请
handleSubmit() { handleSubmit() {
let result = "拒绝" let result = "拒绝";
if(this.result == 'VIA_AUDITING'){ if (this.result == "VIA_AUDITING") {
result = "通过" result = "通过";
} }
this.$refs.form.validate(valid => { this.$refs.form.validate((valid) => {
if (valid) { if (valid) {
this.$Modal.confirm({ this.$Modal.confirm({
title: "确认审核", title: "确认审核",
content: "您确认要审核"+result+"么?", content: "您确认要审核" + result + "么?",
loading: true, loading: true,
onOk: () => { onOk: () => {
auditDistributionCash(this.form.id,{result:this.result}).then(res => { auditDistributionCash(this.form.id, { result: this.result }).then((res) => {
if (res.success) { if (res.success) {
this.$Modal.remove(); this.$Modal.remove();
this.$Message.success("审核成功"); this.$Message.success("审核成功");
this.getDataList(); this.getDataList();
this.modalVisible = false; this.modalVisible = false;
} else { } else {
this.modalVisible = false; this.modalVisible = false;
} }
}); });
} },
}) });
} }
}); });
}, },
// 弹出modal 审核
edit(v) { edit(v) {
this.modalTitle = "审核"; this.modalTitle = "审核";
this.handleStatus = 'edit'; this.handleStatus = "edit";
this.$refs.form.resetFields(); this.$refs.form?.resetFields();
// 转换null为""
for (let attr in v) { for (let attr in v) {
if (v[attr] === null) { if (v[attr] === null) {
v[attr] = ""; v[attr] = "";
@@ -273,12 +222,10 @@ export default {
this.form = JSON.parse(JSON.stringify(v)); this.form = JSON.parse(JSON.stringify(v));
this.modalVisible = true; this.modalVisible = true;
}, },
// 弹出modal 查看 view(v) {
view(v){
this.modalTitle = "查看"; this.modalTitle = "查看";
this.handleStatus = 'view'; this.handleStatus = "view";
this.$refs.form.resetFields(); this.$refs.form?.resetFields();
// 转换null为""
for (let attr in v) { for (let attr in v) {
if (v[attr] === null) { if (v[attr] === null) {
v[attr] = ""; v[attr] = "";
@@ -288,12 +235,11 @@ export default {
let data = JSON.parse(str); let data = JSON.parse(str);
this.form = data; this.form = data;
this.modalVisible = true; this.modalVisible = true;
},
}
}, },
mounted() { mounted() {
this.init(); this.init();
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

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

View File

@@ -1,258 +1,281 @@
<template> <template>
<div> <div>
<Card> <el-card>
<Form ref="searchForm" @keydown.enter.native="handleSearch" :model="searchForm" inline :label-width="70" class="search-form"> <el-form
<Form-item label="订单编号" prop="orderSn"> ref="searchForm"
<Input :model="searchForm"
type="text" inline
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<el-form-item label="订单编号" prop="orderSn">
<el-input
v-model="searchForm.orderSn" v-model="searchForm.orderSn"
placeholder="请输入订单编号" placeholder="请输入订单编号"
clearable clearable
style="width: 240px" style="width: 240px"
/> />
</Form-item> </el-form-item>
<Form-item label="分销商" prop="distributionName"> <el-form-item label="分销商" prop="distributionName">
<Input <el-input
type="text"
v-model="searchForm.distributionName" v-model="searchForm.distributionName"
placeholder="请输入分销商名称" placeholder="请输入分销商名称"
clearable clearable
style="width: 240px" style="width: 240px"
/> />
</Form-item> </el-form-item>
<Form-item label="店铺名称"> <el-form-item label="店铺名称">
<Select v-model="searchForm.storeId" placeholder="请选择" @on-query-change="searchChange" filterable <el-select
clearable style="width: 240px"> v-model="searchForm.storeId"
<Option v-for="item in shopList" :value="item.id" :key="item.id">{{ item.storeName }}</Option> placeholder="请选择"
</Select> filterable
</Form-item> remote
<Form-item label="订单时间"> :remote-method="searchChange"
<DatePicker type="daterange" v-model="timeRange" format="yyyy-MM-dd" placeholder="选择时间" clearable
style="width: 240px"></DatePicker> style="width: 240px"
</Form-item> >
<Button @click="handleSearch" type="primary" icon="ios-search" class="search-btn">搜索</Button> <el-option
</Form> v-for="item in shopList"
</Card> :key="item.id"
<Card> :label="item.storeName"
<Table :loading="loading" border :columns="columns" :data="data" ref="table" class="mt_10"> :value="item.id"
<template slot-scope="{row}" slot="goodsMsg"> />
<div class="goods-msg"> </el-select>
<img :src="row.image" width="60" height="60" alt=""> </el-form-item>
<div> <el-form-item label="订单时间">
<div class="div-zoom"> <el-date-picker
<a @click="linkTo(row.goodsId,row.skuId)">{{row.goodsName}}</a> v-model="timeRange"
</div> type="daterange"
<div style="color:#999;font-size:10px">数量x{{row.num}}</div> value-format="YYYY-MM-DD"
<Poptip trigger="hover" title="扫码在手机中查看" transfer> start-placeholder="开始日期"
<div slot="content"> end-placeholder="结束日期"
<vue-qr :text="wapLinkTo(row.goodsId,row.skuId)" :margin="0" colorDark="#000" colorLight="#fff" :size="150"></vue-qr> 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 class="link-text" @click="linkTo(row.goodsId, row.skuId)">{{ row.goodsName }}</a>
</div> </div>
<img src="../../assets/qrcode.svg" class="hover-pointer" width="20" height="20" alt=""> <div style="color: #999; font-size: 10px">数量x{{ row.num }}</div>
</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> </div>
</div> </template>
</template> </el-table-column>
<template slot-scope="{row}" slot="distributionOrderStatus"> <el-table-column prop="distributionName" label="分销商" min-width="100" show-overflow-tooltip />
<Tag :color="filterStatusColor(row.distributionOrderStatus)">{{filterStatus(row.distributionOrderStatus)}}</Tag> <el-table-column prop="storeName" label="店铺名称" min-width="100" show-overflow-tooltip />
</template> <el-table-column label="状态" min-width="90">
</Table> <template #default="{ row }">
<Row type="flex" justify="end" class="mt_10"> <el-tag v-if="row" :type="filterStatusTagType(row.distributionOrderStatus)">
<Page :current="searchForm.pageNumber" :total="total" :page-size="searchForm.pageSize" {{ filterStatus(row.distributionOrderStatus) }}
@on-change="changePage" @on-page-size-change="changePageSize" :page-size-opts="[20,50,100]" </el-tag>
size="small" show-total show-elevator show-sizer></Page> </template>
</Row> </el-table-column>
</Card> <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> </div>
</template> </template>
<script> <script>
import { import { getDistributionOrder } from "@/api/distribution";
getDistributionOrder import { orderStatusList } from "./dataJson";
} from "@/api/distribution"; import { getShopListData } from "@/api/shops";
import {orderStatusList} from './dataJson' import vueQr from "vue-qr";
import {getShopListData} from '@/api/shops'
import vueQr from 'vue-qr'
export default { export default {
name: "distributionOrder", name: "distributionOrder",
components: { components: { vueQr },
"vue-qr":vueQr data() {
return {
timeRange: [],
orderStatusList,
shopList: [],
distributionId: this.$route.query.id,
loading: true,
searchForm: {
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
},
data: [],
total: 0,
};
},
watch: {
$route(e) {
this.distributionId = e.query.id ? e.query.id : undefined;
this.getDataList();
}, },
data() { },
return { methods: {
timeRange: [], // 范围时间 init() {
orderStatusList, // 订单状态列表 this.getDataList();
shopList: [], // 店铺列表 this.getShopList();
distributionId: this.$route.query.id, // 分销id
loading: true, // 表单加载状态
searchForm: { // 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort:"create_time",
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 // 表单数据总数
};
}, },
methods: { changePage() {
// 初始化数据 this.getDataList();
init() { },
this.getDataList(); changePageSize() {
this.getShopList() this.searchForm.pageNumber = 1;
}, this.getDataList();
//分页 改变页码 },
changePage(v) { handleSearch() {
this.searchForm.pageNumber = v; this.searchForm.pageNumber = 1;
this.getDataList(); this.searchForm.pageSize = 20;
}, this.getDataList();
// 分页 改变页数 },
changePageSize(v) { getDataList() {
this.searchForm.pageSize = v; this.searchForm.distributionId = this.distributionId;
this.getDataList(); this.loading = true;
}, if (this.timeRange && this.timeRange[0] && this.timeRange[1]) {
// 搜索 const startTime = new Date(this.timeRange[0]).getTime();
handleSearch() { const endTime = new Date(this.timeRange[1]).getTime();
this.searchForm.pageNumber = 1; this.searchForm.startTime = this.$filters.unixToDate(startTime / 1000);
this.searchForm.pageSize = 20; this.searchForm.endTime = this.$filters.unixToDate(endTime / 1000);
this.getDataList(); } else {
}, this.searchForm.startTime = null;
// 获取列表数据 this.searchForm.endTime = null;
getDataList() { }
this.searchForm.distributionId = this.distributionId; getDistributionOrder(this.searchForm).then((res) => {
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)
}
// 带多条件搜索参数获取表单数据 请自行修改接口
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; this.loading = false;
}, if (res.success) {
getShopList(val) { // 获取店铺列表 搜索用 this.data = res.result.records;
const params = { this.total = res.result.total;
pageNumber: 1,
pageSize: 20,
storeName: ''
} }
if (val) { });
params.storeName = val;
} else {
params.storeName = ''
}
getShopListData(params).then(res => {
this.shopList = res.result.records
})
},
searchChange(val) { // 店铺搜索,键盘点击回调
this.getShopList(val)
},
filterStatus (status) { // 过滤订单状态
const arr = [
{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 '未完成'; // 默认返回未完成
},
filterStatusColor (status) { // 状态tag标签颜色
const arr = [
{status: 'NO_COMPLETED', color: 'orange'},
{status: 'COMPLETE', color: 'green'},
{status: 'REFUND', color: 'red'},
]
for (let i=0;i<arr.length;i++) {
if (arr[i].status === status) {
return arr[i].color;
}
}
return 'orange'; // 默认返回橙色
}
}, },
mounted() { getShopList(val) {
this.init(); const params = {
pageNumber: 1,
pageSize: 20,
storeName: val || "",
};
getShopListData(params).then((res) => {
this.shopList = res.result.records;
});
}, },
watch: { searchChange(val) {
$route(e) { // 监听路由参数变化调取接口 this.getShopList(val);
this.distributionId = e.query.id ? e.query.id : undefined; },
this.getDataList(); filterStatus(status) {
const arr = [
{ 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 "未完成";
}; },
filterStatusTagType(status) {
const arr = [
{ 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].type;
}
}
return "warning";
},
},
mounted() {
this.init();
},
};
</script> </script>
<style lang="scss">
.goods-msg {
display: flex;
align-items: center;
>div{
margin-left: 10px;
}
}
</style>
<style lang="scss">
.goods-msg {
display: flex;
align-items: center;
> div {
margin-left: 10px;
}
}
.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> <template>
<div style="background-color: #fff;"> <div style="background-color: #fff">
<el-form ref="form" :model="form" :rules="formRule" label-width="140px" style="padding: 10px">
<Form ref="form" :model="form" :rules="formRule" :label-width="120" style="padding: 10px;"> <el-divider content-position="left">分销设置</el-divider>
<el-form-item label="是否开启分销" prop="isOpen">
<Divider orientation="left">分销设置</Divider> <el-switch v-model="form.isOpen" active-text="开启" inactive-text="关闭" />
<FormItem label="是否开启分销" prop="isOpen"> </el-form-item>
<i-switch size="large" v-model="form.isOpen" :true-value="true" :false-value="false"> <el-form-item label="分销关系绑定天数" prop="distributionDay">
<span slot="open">开启</span> <el-input-number v-model="form.distributionDay" :min="1" :max="365" style="width: 120px" />
<span slot="close">关闭</span> </el-form-item>
</i-switch> <el-form-item>
</FormItem> <el-button type="primary" @click="submit">保存</el-button>
<FormItem label="分销关系绑定天数" prop="distributionDay"> </el-form-item>
<InputNumber :min="1" :max="365" style="width:100px;" v-model="form.distributionDay"></InputNumber> </el-form>
</FormItem>
<FormItem>
<Button type="primary" @click="submit">保存</Button>
</FormItem>
</Form>
</div> </div>
</template> </template>
@@ -28,26 +23,22 @@ export default {
data() { data() {
return { return {
form: { form: {
// 添加或编辑表单对象初始化数据
isOpen: true, isOpen: true,
distributionDay: 0, //分销关系绑定天数 distributionDay: 1,
}, },
formRule: { formRule: {
isOpen: [ isOpen: [regular.REQUIRED],
regular.REQUIRED distributionDay: [regular.REQUIRED],
], },
distributionDay: [
regular.REQUIRED
],
}
}; };
}, },
mounted() {
this.init();
},
methods: { methods: {
// 初始化数据
init() { init() {
this.getDataList(); this.getDataList();
}, },
// 获取分销设置数据
getDataList() { getDataList() {
getSetting("DISTRIBUTION_SETTING").then((res) => { getSetting("DISTRIBUTION_SETTING").then((res) => {
if (res.success) { if (res.success) {
@@ -55,20 +46,17 @@ export default {
} }
}); });
}, },
// 提交api
submit() { submit() {
setSetting("DISTRIBUTION_SETTING", this.form).then((res) => { this.$refs.form.validate((valid) => {
if (res.success) { if (!valid) return;
this.$Message.success("操作成功"); setSetting("DISTRIBUTION_SETTING", this.form).then((res) => {
this.getDataList(); if (res.success) {
} this.$Message.success("操作成功");
this.getDataList();
}
});
}); });
}, },
}, },
mounted() {
this.init();
},
}; };
</script> </script>
<style lang="scss" scoped>
</style>

View File

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

View File

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

View File

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

View File

@@ -1,52 +1,67 @@
<template> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Row class="operation"> <div class="operation">
<Button @click="add" type="primary">添加</Button> <el-button type="primary" @click="add">添加</el-button>
<Button @click="delAll">批量删除</Button> <el-button @click="delAll">批量删除</el-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
>
</div> </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> </div>
</template> </template>
@@ -55,156 +70,64 @@ import {
addGoodsUnit, addGoodsUnit,
getGoodsUnitPage, getGoodsUnitPage,
updateGoodsUnit, updateGoodsUnit,
delGoodsUnit delGoodsUnit,
} from "@/api/index"; } from "@/api/index";
import { regular } from "@/utils";
import {regular} from "@/utils";
export default { export default {
name: "goods-unit", name: "goods-unit",
data() { data() {
return { return {
loading: true, // 表单加载状态 loading: true,
modalVisible: false, // 添加或编辑显示 modalVisible: false,
modalTitle: "", // 添加或编辑标题 modalTitle: "",
searchForm: { searchForm: {
// 搜索框初始化对象 pageNumber: 1,
pageNumber: 1, // 当前页数 pageSize: 20,
pageSize: 20, // 页面大小 sort: "createTime",
sort: "createTime", // 默认排序字段 order: "desc",
order: "desc", // 默认排序方式
name: "", name: "",
}, },
form: { form: {
// 添加或编辑表单对象初始化数据
name: "", name: "",
}, },
// 表单验证规则
formValidate: { formValidate: {
name: [ name: [regular.REQUIRED, regular.VARCHAR5],
regular.REQUIRED,
regular.VARCHAR5
]
}, },
submitLoading: false, // 添加或编辑提交状态 submitLoading: false,
selectList: [], // 多选数据 selectList: [],
selectCount: 0, // 多选计数 selectCount: 0,
columns: [ data: [],
// 表头 total: 0,
{
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, // 表单数据总数
}; };
}, },
methods: { methods: {
// 初始化数据
init() { init() {
this.getDataList(); this.getDataList();
}, },
// 分页 改变页码
changePage(v) { changePage(v) {
this.searchForm.pageNumber = v; this.searchForm.pageNumber = v;
this.getDataList(); this.getDataList();
this.clearSelectAll(); this.clearSelectAll();
}, },
// 分页 改变页数
changePageSize(v) { changePageSize(v) {
this.searchForm.pageSize = v; this.searchForm.pageSize = v;
this.getDataList(); this.getDataList();
}, },
// 搜索
handleSearch() { handleSearch() {
this.searchForm.pageNumber = 1; this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20; this.searchForm.pageSize = 20;
this.getDataList(); this.getDataList();
}, },
// 清除选中
clearSelectAll() { clearSelectAll() {
this.$refs.table.selectAll(false); this.$refs.table?.clearSelection();
}, },
// 选中回调
changeSelect(e) { changeSelect(e) {
this.selectList = e; this.selectList = e;
this.selectCount = e.length; this.selectCount = e.length;
}, },
// 获取列表数据
getDataList() { getDataList() {
this.loading = true; this.loading = true;
getGoodsUnitPage(this.searchForm).then((res) => { getGoodsUnitPage(this.searchForm).then((res) => {
this.loading = false; this.loading = false;
if (res.success) { if (res.success) {
@@ -215,20 +138,16 @@ export default {
this.total = this.data.length; this.total = this.data.length;
this.loading = false; this.loading = false;
}, },
// 修改后提交
handleSubmit() { handleSubmit() {
this.$refs.form.validate((valid) => { this.$refs.form.validate((valid) => {
if (valid) { if (valid) {
this.submitLoading = true; this.submitLoading = true;
if (this.modalTitle == "添加") { if (this.modalTitle == "添加") {
if (this.data.find((item) => item.name == this.form.name)) {
if(this.data.find(item=>item.name == this.form.name)){ this.$Message.error("请勿添加重复计量单位!");
this.$Message.error('请勿添加重复计量单位!') this.submitLoading = false;
this.submitLoading = false return;
return
} }
// 添加 避免编辑后传入id等数据 记得删除
delete this.form.id; delete this.form.id;
addGoodsUnit(this.form).then((res) => { addGoodsUnit(this.form).then((res) => {
this.submitLoading = false; this.submitLoading = false;
@@ -239,7 +158,6 @@ export default {
} }
}); });
} else { } else {
// 编辑
updateGoodsUnit(this.id, this.form).then((res) => { updateGoodsUnit(this.id, this.form).then((res) => {
this.submitLoading = false; this.submitLoading = false;
if (res.success) { if (res.success) {
@@ -252,30 +170,24 @@ export default {
} }
}); });
}, },
// 添加
add() { add() {
this.modalTitle = "添加"; this.modalTitle = "添加";
this.form = {}; this.form = {};
this.$refs.form.resetFields(); this.$refs.form?.resetFields();
this.modalVisible = true; this.modalVisible = true;
}, },
// 编辑
edit(v) { edit(v) {
this.id = v.id; this.id = v.id;
this.modalTitle = "修改"; this.modalTitle = "修改";
this.modalVisible = true; this.modalVisible = true;
this.form.name = v.name; this.form.name = v.name;
}, },
// 删除
remove(v) { remove(v) {
this.$Modal.confirm({ this.$Modal.confirm({
title: "确认删除", title: "确认删除",
// 记得确认修改此处
content: "您确认要删除 " + v.name + " ?", content: "您确认要删除 " + v.name + " ?",
loading: true, loading: true,
onOk: () => { onOk: () => {
// 删除
delGoodsUnit(v.id).then((res) => { delGoodsUnit(v.id).then((res) => {
this.$Modal.remove(); this.$Modal.remove();
if (res.success) { if (res.success) {
@@ -286,8 +198,6 @@ export default {
}, },
}); });
}, },
// 全部删除
delAll() { delAll() {
if (this.selectCount <= 0) { if (this.selectCount <= 0) {
this.$Message.warning("您还未选择要删除的数据"); this.$Message.warning("您还未选择要删除的数据");
@@ -303,7 +213,6 @@ export default {
ids += e.id + ","; ids += e.id + ",";
}); });
ids = ids.substring(0, ids.length - 1); ids = ids.substring(0, ids.length - 1);
// 批量删除
delGoodsUnit(ids).then((res) => { delGoodsUnit(ids).then((res) => {
this.$Modal.remove(); this.$Modal.remove();
if (res.success) { if (res.success) {
@@ -312,9 +221,9 @@ export default {
this.getDataList(); this.getDataList();
} }
}); });
} },
}); });
} },
}, },
mounted() { mounted() {
this.init(); this.init();

View File

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

View File

@@ -1,79 +1,93 @@
<template> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Form <el-form
ref="searchForm" ref="searchForm"
@keydown.enter.native="handleSearch"
:model="searchForm" :model="searchForm"
inline inline
:label-width="70" label-width="70px"
class="search-form" class="search-form"
@keyup.enter="handleSearch"
> >
<Form-item label="商品名称" prop="goodsName"> <el-form-item label="商品名称" prop="goodsName">
<Input <el-input
type="text"
v-model="searchForm.goodsName" v-model="searchForm.goodsName"
placeholder="请输入商品名称" placeholder="请输入商品名称"
clearable clearable
style="width: 240px" style="width: 240px"
/> />
</Form-item> </el-form-item>
<Form-item label="商品编号" prop="id"> <el-form-item label="商品编号" prop="id">
<Input <el-input
type="text"
v-model="searchForm.id" v-model="searchForm.id"
placeholder="请输入商品编号" placeholder="请输入商品编号"
clearable clearable
style="width: 240px" style="width: 240px"
/> />
</Form-item> </el-form-item>
<Button @click="handleSearch" class="search-btn" type="primary" icon="ios-search" <el-form-item>
>搜索</Button <el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
> </el-form-item>
</Form> </el-form>
</Card> </el-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>
<div style="margin-left: 13px"> <el-card>
<div class="div-zoom"> <el-table v-loading="loading" :data="data" ref="table" class="mt_10" style="width: 100%">
<a>{{ scope.row.goodsName }}</a> <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 class="link-text">{{ row.goodsName }}</a>
</div>
</div> </div>
</div> </div>
</div> </template>
</template> </el-table-column>
</Table> <el-table-column prop="id" label="商品编号" min-width="120" show-overflow-tooltip />
<Row type="flex" justify="end" class="mt_10"> <el-table-column label="价格" min-width="120">
<Page <template #default="{ row }">
:current="searchForm.pageNumber" <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" :total="total"
:page-size="searchForm.pageSize" layout="total, sizes, prev, pager, next, jumper"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small" size="small"
show-total @current-change="changePage"
show-elevator @size-change="changePageSize"
show-sizer />
></Page> </div>
</Row> </el-card>
</Card>
</div> </div>
</template> </template>
@@ -81,214 +95,82 @@
import { authGoods, getAuthGoodsListData } from "@/api/goods"; import { authGoods, getAuthGoodsListData } from "@/api/goods";
export default { export default {
name: "goods", name: "goodsApply",
components: {},
data() { data() {
return { return {
id: "", //要操作的id loading: true,
loading: true, // 表单加载状态
searchForm: { searchForm: {
// 搜索框初始化对象 pageNumber: 1,
pageNumber: 1, // 当前页数 pageSize: 20,
pageSize: 20, // 页面大小 sort: "create_time",
sort: "create_time", // 默认排序字段 order: "desc",
order: "desc", // 默认排序方式
}, },
goodsAuditForm: { goodsAuditForm: {
// 商品编辑表单
auth_flag: 1, auth_flag: 1,
}, },
columns: [ data: [],
{ total: 0,
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, // 表单数据总数
}; };
}, },
methods: { 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() { init() {
// 初始化数据
this.getDataList(); this.getDataList();
}, },
changePage(v) { changePage(v) {
// 改变页码
this.searchForm.pageNumber = v; this.searchForm.pageNumber = v;
this.getDataList(); this.getDataList();
}, },
changePageSize(v) { changePageSize(v) {
// 改变每页数量
this.searchForm.pageSize = v; this.searchForm.pageSize = v;
this.getDataList(); this.getDataList();
}, },
handleSearch() { handleSearch() {
// 搜索
this.searchForm.pageNumber = 1; this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20; this.searchForm.pageSize = 20;
this.getDataList(); this.getDataList();
}, },
getDataList() { getDataList() {
// 获取列表数据
this.loading = true; this.loading = true;
// 带多条件搜索参数获取表单数据
this.searchForm.authFlag = 0; this.searchForm.authFlag = 0;
getAuthGoodsListData(this.searchForm).then((res) => { getAuthGoodsListData(this.searchForm)
this.loading = false; .then((res) => {
if (res.success) { this.loading = false;
this.data = res.result.records; if (res.success) {
this.total = res.result.total; this.data = res.result.records;
} this.total = res.result.total;
}); }
})
.catch(() => {
this.loading = false;
});
}, },
examine(v, authFlag) { examine(v, authFlag) {
// 审核商品 const examine = authFlag === 1 ? "通过" : "拒绝";
let examine = "通过"; this.goodsAuditForm.authFlag = authFlag === 1 ? "PASS" : "REFUSE";
this.goodsAuditForm.authFlag = "PASS";
if (authFlag != 1) {
examine = "拒绝";
this.goodsAuditForm.authFlag = "REFUSE";
}
this.$Modal.confirm({ this.$Modal.confirm({
title: "确认审核", title: "确认审核",
content: "您确认要审核" + examine + " " + v.goodsName + " ?", content: `您确认要审核${examine} ${v.goodsName} ?`,
loading: true, loading: true,
onOk: () => { onOk: () => {
let formData = new FormData(); const formData = new FormData();
formData.append('goodsIds', v.id); formData.append("goodsIds", v.id);
formData.append('authFlag', this.goodsAuditForm.authFlag); formData.append("authFlag", this.goodsAuditForm.authFlag);
authGoods(formData).then((res) => { authGoods(formData).then((res) => {
this.$Modal.remove(); this.$Modal.remove();
if (res.success) { if (res.success) {
@@ -299,13 +181,11 @@ export default {
}, },
}); });
}, },
//查看商品详情
showDetail(v) { showDetail(v) {
let id = v.id; this.$filters.customRouterPush({
this.$options.filters.customRouterPush({
name: "goods-detail", name: "goods-detail",
query: { id: id }, query: { id: v.id },
}) });
}, },
}, },
mounted() { mounted() {
@@ -313,3 +193,20 @@ export default {
}, },
}; };
</script> </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> <template>
<div> <div>
<Form :label-width="120"> <el-form label-width="120px">
<Card> <el-card>
<div class="base-info-item"> <div class="base-info-item">
<h4>基本信息</h4> <h4>基本信息</h4>
<div class="form-item-view"> <div class="form-item-view">
<FormItem label="商品分类"> <el-form-item label="商品分类">
<span v-for="(item, index) in goods.categoryName" :key="index"> <span v-for="(item, index) in goods.categoryName" :key="index">
{{ item }} {{ item }}
<i v-if="index !== goods.categoryName.length - 1">&gt;</i> <i v-if="index !== goods.categoryName.length - 1">&gt;</i>
</span> </span>
</FormItem> </el-form-item>
<FormItem label="商品名称"> <el-form-item label="商品名称">{{ goods.goodsName }}</el-form-item>
{{ goods.goodsName }} <el-form-item label="商品卖点">{{ goods.sellingPoint }}</el-form-item>
</FormItem> <el-form-item label="商品参数">
<div
<FormItem label="商品卖点"> v-if="goods.goodsParamsDTOList && goods.goodsParamsDTOList.length"
{{ goods.sellingPoint }} v-for="(item, index) in goods.goodsParamsDTOList"
</FormItem> :key="index"
<FormItem label="商品参数"> style="margin-bottom: 10px; display: flex; align-items: center"
<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 }} :
{{ item.groupName }} : <tag v-for="(child,i) in item.goodsParamsItemDTOList" :key="i"> <el-tag v-for="(child, i) in item.goodsParamsItemDTOList" :key="i" style="margin-left: 4px">
{{ child.paramName }} - {{ child.paramValue }} {{ child.paramName }} - {{ child.paramValue }}
</tag> </el-tag>
</div> </div>
</div> </el-form-item>
</FormItem>
</div> </div>
<h4>商品交易信息</h4> <h4>商品交易信息</h4>
<div class="form-item-view"> <div class="form-item-view">
<FormItem label="计量单位"> {{ goods.goodsUnit }}</FormItem> <el-form-item label="计量单位">{{ goods.goodsUnit }}</el-form-item>
<FormItem label="销售模式"> <el-form-item label="销售模式">
{{ goods.salesModel === "RETAIL" ? "零售型" : "批发型" }} {{ goods.salesModel === "RETAIL" ? "零售型" : "批发型" }}
</FormItem> </el-form-item>
<FormItem label="销售规则" v-if="goods.salesModel !== 'RETAIL'"> <el-form-item v-if="goods.salesModel !== 'RETAIL'" label="销售规则">
<Table <el-table border :data="wholesaleData" style="width: 100%">
border <el-table-column label="销售规则" width="300">
:columns="wholesalePreviewColumns" <template #default="{ row }">
:data="wholesaleData" <span v-if="row">
> 当商品购买数量 {{ row.num }} 售价为 {{ row.price }} /{{ goods.goodsUnit }}</span>
</Table> </template>
</FormItem> </el-table-column>
</el-table>
</el-form-item>
</div> </div>
<h4>商品规格及图片</h4> <h4>商品规格及图片</h4>
<div class="form-item-view"> <div class="form-item-view">
<FormItem label="商品编号"> {{ goods.id }}</FormItem> <el-form-item label="商品编号">{{ goods.id }}</el-form-item>
<FormItem label="商品价格"> <el-form-item label="商品价格">
<priceColorScheme :value="goods.price" :color="$mainColor"></priceColorScheme> <priceColorScheme :value="goods.price" :color="$mainColor" />
</el-form-item>
</FormItem> <el-form-item label="商品图片">
<FormItem label="商品图片">
<div <div
class="demo-upload-list"
v-for="(item, __index) in goods.goodsGalleryList" v-for="(item, __index) in goods.goodsGalleryList"
:key="__index" :key="__index"
class="demo-upload-list"
> >
<img :src="item" /> <img :src="item" />
<div class="demo-upload-list-cover"> <div class="demo-upload-list-cover">
<Icon <el-icon class="preview-icon" @click="handleViewGoodsPicture(item)"><View /></el-icon>
type="ios-eye-outline"
@click.native="handleViewGoodsPicture(item)"
></Icon>
</div> </div>
<Modal title="View Image" v-model="goodsPictureVisible">
<img
:src="previewGoodsPicture"
v-if="goodsPictureVisible"
style="width: 100%"
/>
</Modal>
</div> </div>
</FormItem> <el-dialog v-model="goodsPictureVisible" title="View Image" width="600px">
<FormItem label="商品视频"> <img v-if="goodsPictureVisible" :src="previewGoodsPicture" style="width: 100%" />
<video </el-dialog>
v-if="goods.goodsVideo" </el-form-item>
controls <el-form-item label="商品视频">
class="player" <video v-if="goods.goodsVideo" controls class="player" :src="goods.goodsVideo" />
:src="goods.goodsVideo" </el-form-item>
/> <el-form-item label="商品规格">
</FormItem> <el-table :data="skuData" style="width: 100%">
<FormItem label="商品规格"> <el-table-column prop="specs" label="规格" />
<Table :columns="skuColumn" :data="skuData"> <el-table-column prop="sn" label="编号" />
<template slot="showImage" slot-scope="scope"> <el-table-column prop="weight" label="重量(kg)" />
<div style="margin-top: 5px; display: flex"> <el-table-column
<div> 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 <img
v-for="(item,index) in scope.row.image" v-for="(item, index) in row.image"
:key="index" :key="index"
:src="item" :src="item"
style="height: 60px; margin:10px; width: 60px" style="height: 60px; margin: 10px; width: 60px"
/> />
</div> </div>
</div> </template>
</template> </el-table-column>
<template slot-scope="{ row }" slot="wholePrice0"> </el-table>
<Input </el-form-item>
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>
</div> </div>
<h4>商品详情描述</h4> <h4>商品详情描述</h4>
<div class="form-item-view"> <div class="form-item-view">
<FormItem label="商品描述"> <el-form-item label="商品描述">
<div v-html="goods.intro"></div> <div v-html="goods.intro"></div>
</FormItem> </el-form-item>
<FormItem label="移动端描述"> <el-form-item label="移动端描述">
<div v-html="goods.mobileIntro"></div> <div v-html="goods.mobileIntro"></div>
</FormItem> </el-form-item>
</div> </div>
</div> </div>
</Card> </el-card>
</Form> </el-form>
</div> </div>
</template> </template>
<script> <script>
import { View } from "@element-plus/icons-vue";
import { getGoodsDetail } from "@/api/goods"; import { getGoodsDetail } from "@/api/goods";
export default { export default {
name: "goodsDetail", name: "goodsDetail",
components: { View },
data() { data() {
return { return {
goods: {}, // 商品信息 goods: {},
previewGoodsPicture: "", // 预览图片 previewGoodsPicture: "",
goodsPictureVisible: false, // 预览图片模态框 goodsPictureVisible: false,
wholesalePreviewColumns: [
{
title: "销售规则",
width: 300,
render: (h, params) => {
let guide =
"当商品购买数量 ≥" +
params.row.num +
" 时,售价为 ¥" +
params.row.price +
" /" +
this.goods.goodsUnit;
return h("div", guide);
},
},
],
wholesaleData: [], wholesaleData: [],
skuColumn: [ skuData: [],
// 规格表头
{
title: "规格",
key: "specs",
},
{
title: "编号",
key: "sn",
},
{
title: "重量(kg)",
key: "weight",
},
],
skuData: [], // sku数据
}; };
}, },
mounted() { mounted() {
this.initGoods(this.$route.query.id); this.initGoods(this.$route.query.id);
}, },
methods: { methods: {
// 初始化数据,获取商品详情
initGoods(id) { initGoods(id) {
getGoodsDetail(id).then((res) => { getGoodsDetail(id).then((res) => {
this.goods = res.result; this.goods = res.result;
let that = this; this.skuData = res.result.skuList.map((sku) => ({
res.result.skuList.forEach(function (sku, index, array) { specs: sku.goodsName,
that.skuData.push({ sn: sku.sn,
specs: sku.goodsName, weight: sku.weight,
sn: sku.sn, cost: sku.cost,
weight: sku.weight, price: sku.price,
cost: sku.cost, image: sku.goodsGalleryList,
price:sku.price, quantity: sku.quantity,
image: sku.goodsGalleryList, }));
quantity:sku.quantity this.wholesaleData = res.result.wholesaleList || [];
});
});
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;
}); });
}, },
// 预览商品图片
handleViewGoodsPicture(url) { handleViewGoodsPicture(url) {
this.previewGoodsPicture = url; this.previewGoodsPicture = url;
this.goodsPictureVisible = true; this.goodsPictureVisible = true;
@@ -251,7 +163,6 @@ export default {
</script> </script>
<style lang="scss" soped> <style lang="scss" soped>
/*平铺*/
div.base-info-item { div.base-info-item {
h4 { h4 {
margin-bottom: 10px; margin-bottom: 10px;
@@ -300,13 +211,10 @@ div.base-info-item {
.demo-upload-list:hover .demo-upload-list-cover { .demo-upload-list:hover .demo-upload-list-cover {
display: block; display: block;
} }
.demo-upload-list-cover i { .preview-icon {
color: #fff; color: #fff;
font-size: 20px; font-size: 20px;
cursor: pointer; cursor: pointer;
margin: 0 2px; margin-top: 20px;
}
.ivu-table table {
width: 100% !important;
} }
</style> </style>

View File

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

View File

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

View File

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

View File

@@ -1,55 +1,72 @@
<template> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Form <el-form
ref="searchForm" ref="searchForm"
@submit.native.prevent
@keydown.enter.native="handleSearch"
:model="searchForm" :model="searchForm"
inline inline
:label-width="70" label-width="70px"
class="search-form" class="search-form"
@keyup.enter="handleSearch"
> >
<Form-item label="参数名称"> <el-form-item label="参数名称">
<Input <el-input
type="text"
v-model="searchForm.paramName" v-model="searchForm.paramName"
placeholder="请输入参数名称" placeholder="请输入参数名称"
clearable clearable
style="width: 240px" style="width: 240px"
/> />
</Form-item> </el-form-item>
<Button @click="handleSearch" type="primary">搜索</Button> <el-form-item>
</Form> <el-button type="primary" @click="handleSearch">搜索</el-button>
</Card> </el-form-item>
</el-form>
</el-card>
<Card> <el-card>
<Row class="operation padding-row"> <div class="operation padding-row">
<Button @click="goAdd" type="primary">添加</Button> <el-button type="primary" @click="goAdd">添加</el-button>
</Row> </div>
<Table :loading="loading" border :columns="columns" :data="data" ref="table"></Table> <el-table v-loading="loading" border :data="data" ref="table" style="width: 100%">
<Row type="flex" justify="end" class="mt_10"> <el-table-column prop="paramName" label="参数名称" width="300" />
<Page <el-table-column prop="options" label="参数值" min-width="260" show-overflow-tooltip />
:current="searchForm.pageNumber" <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" :total="total"
:page-size="searchForm.pageSize" layout="total, sizes, prev, pager, next, jumper"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small" size="small"
show-total @current-change="changePage"
show-elevator @size-change="changePageSize"
show-sizer />
></Page> </div>
</Row> </el-card>
</Card>
</div> </div>
</template> </template>
<script> <script>
import { import { deleteParams, getGoodsParamsPage } from "@/api/goods";
deleteParams,
getGoodsParamsPage,
} from "@/api/goods";
export default { export default {
name: "categoryParams", name: "categoryParams",
@@ -66,90 +83,12 @@ export default {
paramName: "", paramName: "",
}, },
data: [], 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: { methods: {
isTruthyFlag(val) {
return val === 1 || val === "1" || val === true;
},
init() { init() {
this.getDataList(); this.getDataList();
}, },
@@ -209,4 +148,10 @@ export default {
}, },
}; };
</script> </script>
<style lang="scss"></style> <style lang="scss">
.link-text {
color: #409eff;
cursor: pointer;
text-decoration: none;
}
</style>

View File

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

View File

@@ -1,61 +1,70 @@
<template> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Row class="operation padding-row"> <div class="operation padding-row">
<Button type="primary" @click="openAdd">添加分组</Button> <el-button type="primary" @click="openAdd">添加分组</el-button>
</Row> </div>
<Table
:loading="loading" <el-table ref="table" v-loading="loading" :data="data" border class="mt_10" style="width: 100%">
border <el-table-column prop="groupName" label="分组名称" min-width="160" show-overflow-tooltip />
:columns="columns" <el-table-column prop="description" label="分组描述" min-width="240" show-overflow-tooltip />
:data="data" <el-table-column prop="createTime" label="创建时间" width="180" />
ref="table" <el-table-column prop="updateTime" label="更新时间" width="180" />
class="mt_10" <el-table-column label="操作" width="200" align="center" fixed="right">
></Table> <template #default="{ row }">
<Row type="flex" justify="end" class="mt_10"> <div v-if="row" class="ops" style="display: flex; justify-content: center">
<Page <a class="link-text" @click="openEdit(row)">编辑</a>
:current="searchForm.pageNumber" <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" :total="total"
:page-size="searchForm.pageSize" layout="total, sizes, prev, pager, next, jumper"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small" size="small"
show-total @current-change="changePage"
show-elevator @size-change="changePageSize"
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>
</div> </div>
</Modal> </el-card>
<Modal v-model="editFlag" title="编辑商品分组">
<Form ref="editForm" :model="formEdit" :rules="rulesEdit" :label-width="90"> <el-dialog v-model="addFlag" title="添加商品分组" width="500px" :close-on-click-modal="false" destroy-on-close>
<Input v-model="formEdit.id" v-show="false" /> <el-form ref="addForm" :model="formAdd" :rules="rulesAdd" label-width="90px">
<FormItem label="分组名称" prop="groupName" style="width: 90%;"> <el-form-item label="分组名称" prop="groupName" style="width: 90%">
<Input v-model="formEdit.groupName" maxlength="30" placeholder="请输入分组名称" /> <el-input v-model="formAdd.groupName" maxlength="30" placeholder="请输入分组名称" />
</FormItem> </el-form-item>
<FormItem label="分组描述" prop="description" style="width: 90%;"> <el-form-item label="分组描述" prop="description" style="width: 90%">
<Input v-model="formEdit.description" maxlength="200" placeholder="请输入分组描述" /> <el-input v-model="formAdd.description" maxlength="200" placeholder="请输入分组描述" />
</FormItem> </el-form-item>
</Form> </el-form>
<div slot="footer"> <template #footer>
<Button @click="editFlag = false">取消</Button> <el-button @click="addFlag = false">取消</el-button>
<Button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</Button> <el-button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</el-button>
</div> </template>
</Modal> </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> </div>
</template> </template>
@@ -71,54 +80,6 @@ export default {
pageNumber: 1, pageNumber: 1,
pageSize: 20, 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: [], data: [],
total: 0, total: 0,
addFlag: false, addFlag: false,
@@ -135,10 +96,10 @@ export default {
description: "", description: "",
}, },
rulesAdd: { rulesAdd: {
groupName: [{ required: true, message: "请输入分组名称" }], groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
}, },
rulesEdit: { rulesEdit: {
groupName: [{ required: true, message: "请输入分组名称" }], groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
}, },
}; };
}, },
@@ -248,7 +209,7 @@ export default {
remove(row) { remove(row) {
this.$Modal.confirm({ this.$Modal.confirm({
title: "提示", title: "提示",
content: "<p>确定删除该分组?</p>", content: "确定删除该分组?",
onOk: () => { onOk: () => {
API_Goods.deleteGoodsGroup(row.id) API_Goods.deleteGoodsGroup(row.id)
.then((res) => { .then((res) => {
@@ -271,3 +232,16 @@ export default {
}, },
}; };
</script> </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> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Form <el-form
ref="searchForm" ref="searchForm"
:model="searchForm" :model="searchForm"
inline inline
:label-width="70" label-width="70px"
class="search-form" class="search-form"
@keydown.enter.native="handleSearch" @keyup.enter="handleSearch"
> >
<Form-item label="商品名称" prop="goodsName"> <el-form-item label="商品名称" prop="goodsName">
<Input <el-input
v-model="searchForm.goodsName" v-model="searchForm.goodsName"
placeholder="请输入商品名称" placeholder="请输入商品名称"
clearable clearable
style="width: 240px" style="width: 240px"
/> />
</Form-item> </el-form-item>
<Form-item label="商品ID" prop="goodsId"> <el-form-item label="商品ID" prop="goodsId">
<Input <el-input
v-model="searchForm.goodsId" v-model="searchForm.goodsId"
placeholder="请输入商品ID" placeholder="请输入商品ID"
clearable clearable
style="width: 240px" style="width: 240px"
/> />
</Form-item> </el-form-item>
<Form-item label="SKU货号" prop="sn"> <el-form-item label="SKU货号" prop="sn">
<Input <el-input
v-model="searchForm.sn" v-model="searchForm.sn"
placeholder="请输入SKU货号" placeholder="请输入SKU货号"
clearable clearable
style="width: 240px" style="width: 240px"
/> />
</Form-item> </el-form-item>
<Form-item label="店铺名称" prop="storeName"> <el-form-item label="店铺名称" prop="storeName">
<Input <el-input
v-model="searchForm.storeName" v-model="searchForm.storeName"
placeholder="请输入店铺名称" placeholder="请输入店铺名称"
clearable clearable
style="width: 240px" style="width: 240px"
/> />
</Form-item> </el-form-item>
<Button <el-form-item>
@click="handleSearch" <el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
class="search-btn" <el-button style="margin-left: 8px" @click="handleReset">重置</el-button>
type="primary" </el-form-item>
icon="ios-search" </el-form>
> </el-card>
搜索
</Button>
<Button @click="handleReset" style="margin-left: 8px">重置</Button>
</Form>
</Card>
<Card class="mt_10"> <el-card class="mt_10">
<Alert show-icon> <el-alert
支持按规格逐条设置虚拟销量列表总销量 = 真实销量 + 虚拟销量 type="info"
</Alert> show-icon
:closable="false"
title="支持按规格逐条设置虚拟销量,列表总销量 = 真实销量 + 虚拟销量。"
style="margin-bottom: 10px"
/>
<div class="batch-operations"> <div class="batch-operations">
<Button <el-button
type="primary" type="primary"
:disabled="selectedRows.length === 0" :disabled="selectedRows.length === 0"
@click="openBatchVirtualSalesModal" @click="openBatchVirtualSalesModal"
> >
批量设置虚拟销量 批量设置虚拟销量
</Button> </el-button>
</div> </div>
<Table <el-table
:loading="loading"
:columns="columns"
:data="data"
ref="table" ref="table"
v-loading="loading"
:data="data"
class="mt_10" class="mt_10"
sortable="custom" style="width: 100%"
@on-sort-change="changeSort" @sort-change="changeSort"
@on-selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<template slot="goodsSlot" slot-scope="{ row }"> <el-table-column type="selection" width="55" align="center" />
<div class="goods-info"> <el-table-column prop="id" label="SKU ID" width="180" show-overflow-tooltip />
<img <el-table-column prop="goodsId" label="商品ID" width="180" show-overflow-tooltip />
:src="row.thumbnail" <el-table-column label="商品信息" min-width="360">
class="goods-thumbnail" <template #default="{ row }">
/> <div v-if="row" class="goods-info">
<div class="goods-text"> <img
<div class="div-zoom">{{ row.goodsName }}</div> v-if="row.thumbnail"
<div class="sub-title" v-if="row.simpleSpecs"> :src="row.thumbnail"
规格{{ row.simpleSpecs.trim() || "-" }} class="goods-thumbnail"
alt=""
/>
<div class="goods-text">
<div class="div-zoom">{{ row.goodsName }}</div>
<div v-if="row.simpleSpecs" class="sub-title">
规格{{ row.simpleSpecs.trim() || "-" }}
</div>
</div> </div>
</div> </div>
</div> </template>
</template> </el-table-column>
</Table> <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"> <div class="mt_10" style="display: flex; justify-content: flex-end">
<Page <el-pagination
:current="searchForm.pageNumber" v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total" :total="total"
:page-size="searchForm.pageSize" layout="total, sizes, prev, pager, next, jumper"
:page-size-opts="[20, 50, 100]"
size="small" size="small"
show-total @current-change="changePage"
show-elevator @size-change="changePageSize"
show-sizer />
@on-change="changePage" </div>
@on-page-size-change="changePageSize" </el-card>
></Page>
</Row>
</Card>
<Modal <el-dialog
v-model="editModalVisible" v-model="editModalVisible"
:title="modalMode === 'batch' ? '批量设置虚拟销量' : '设置虚拟销量'" :title="modalMode === 'batch' ? '批量设置虚拟销量' : '设置虚拟销量'"
:mask-closable="false" width="480px"
:closable="!modalSubmitting" :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 v-if="modalMode === 'single' && currentSku" class="virtual-sales-modal">
<div class="sku-info-item"> <div class="sku-info-item">
@@ -129,50 +169,42 @@
<span class="sku-info-label">SKU ID</span> <span class="sku-info-label">SKU ID</span>
<span>{{ currentSku.id || "-" }}</span> <span>{{ currentSku.id || "-" }}</span>
</div> </div>
<el-form label-width="90px" class="mt_10">
<Form :label-width="90" class="mt_10"> <el-form-item label="虚拟销量">
<Form-item label="虚拟销量"> <el-input-number
<InputNumber
v-model="editForm.virtualSales" v-model="editForm.virtualSales"
:min="0" :min="0"
:max="99999999" :max="99999999"
:precision="0" :precision="0"
style="width: 200px" style="width: 200px"
/> />
</Form-item> </el-form-item>
</Form> </el-form>
</div> </div>
<div v-else-if="modalMode === 'batch'" class="virtual-sales-modal"> <div v-else-if="modalMode === 'batch'" class="virtual-sales-modal">
<div class="sku-info-item"> <div class="sku-info-item">
<span class="sku-info-label">已选规格</span> <span class="sku-info-label">已选规格</span>
<span>{{ selectedRows.length }} </span> <span>{{ selectedRows.length }} </span>
</div> </div>
<el-form label-width="90px" class="mt_10">
<Form :label-width="90" class="mt_10"> <el-form-item label="虚拟销量">
<Form-item label="虚拟销量"> <el-input-number
<InputNumber
v-model="editForm.virtualSales" v-model="editForm.virtualSales"
:min="0" :min="0"
:max="99999999" :max="99999999"
:precision="0" :precision="0"
style="width: 200px" style="width: 200px"
/> />
</Form-item> </el-form-item>
</Form> </el-form>
</div> </div>
<div slot="footer"> <template #footer>
<Button @click="handleCancelVirtualSales" :disabled="modalSubmitting"> <el-button :disabled="modalSubmitting" @click="handleCancelVirtualSales">取消</el-button>
取消 <el-button type="primary" :loading="modalSubmitting" @click="handleSubmitVirtualSales">
</Button>
<Button
type="primary"
:loading="modalSubmitting"
@click="handleSubmitVirtualSales"
>
提交 提交
</Button> </el-button>
</div> </template>
</Modal> </el-dialog>
</div> </div>
</template> </template>
@@ -216,84 +248,6 @@ export default {
sn: "", sn: "",
storeName: "", 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: [], data: [],
total: 0, total: 0,
}; };
@@ -328,12 +282,13 @@ export default {
}; };
this.getDataList(); this.getDataList();
}, },
changeSort(e) { changeSort({ prop, order }) {
this.searchForm.sort = e.key; if (!order) {
this.searchForm.order = e.order;
if (e.order === "normal") {
this.searchForm.sort = "create_time"; this.searchForm.sort = "create_time";
this.searchForm.order = "desc"; this.searchForm.order = "desc";
} else {
this.searchForm.sort = prop;
this.searchForm.order = order === "ascending" ? "asc" : "desc";
} }
this.getDataList(); this.getDataList();
}, },
@@ -394,9 +349,7 @@ export default {
this.editModalVisible = true; this.editModalVisible = true;
}, },
handleCancelVirtualSales() { handleCancelVirtualSales() {
if (this.modalSubmitting) { if (this.modalSubmitting) return;
return;
}
this.resetVirtualSalesModal(); this.resetVirtualSalesModal();
}, },
resetVirtualSalesModal() { resetVirtualSalesModal() {
@@ -416,18 +369,21 @@ export default {
this.handleBatchSubmitVirtualSales(virtualSales); this.handleBatchSubmitVirtualSales(virtualSales);
return; return;
} }
if (!this.currentSku || this.currentIndex < 0) { if (!this.currentSku || this.currentIndex < 0) return;
return;
}
const currentIndex = this.currentIndex; const currentIndex = this.currentIndex;
const currentSkuId = this.currentSku.id; const currentSkuId = this.currentSku.id;
this.modalSubmitting = true; this.modalSubmitting = true;
this.$set(this.data[currentIndex], "saving", true); if (this.data[currentIndex]) {
this.data[currentIndex].saving = true;
}
updateGoodsSkuVirtualSales(currentSkuId, { virtualSales }) updateGoodsSkuVirtualSales(currentSkuId, { virtualSales })
.then((res) => { .then((res) => {
if (res && res.success) { if (res && res.success) {
this.$set(this.data[currentIndex], "virtualSales", virtualSales); if (this.data[currentIndex]) {
this.$set(this.data[currentIndex], "virtualSalesInput", virtualSales); this.data[currentIndex].virtualSales = virtualSales;
this.data[currentIndex].virtualSalesInput = virtualSales;
}
this.$Message.success("虚拟销量设置成功"); this.$Message.success("虚拟销量设置成功");
this.resetVirtualSalesModal(); this.resetVirtualSalesModal();
} else { } else {
@@ -439,7 +395,7 @@ export default {
}) })
.finally(() => { .finally(() => {
if (this.data[currentIndex]) { if (this.data[currentIndex]) {
this.$set(this.data[currentIndex], "saving", false); this.data[currentIndex].saving = false;
} }
this.modalSubmitting = false; this.modalSubmitting = false;
}); });
@@ -455,9 +411,7 @@ export default {
.then((res) => { .then((res) => {
if (res && res.success) { if (res && res.success) {
this.data = this.data.map((item) => { this.data = this.data.map((item) => {
if (!skuIds.includes(item.id)) { if (!skuIds.includes(item.id)) return item;
return item;
}
return { return {
...item, ...item,
virtualSales, virtualSales,
@@ -465,9 +419,7 @@ export default {
}; };
}); });
this.selectedRows = []; this.selectedRows = [];
if (this.$refs.table) { this.$refs.table?.clearSelection();
this.$refs.table.selectAll(false);
}
this.$Message.success("虚拟销量设置成功"); this.$Message.success("虚拟销量设置成功");
this.resetVirtualSalesModal(); this.resetVirtualSalesModal();
} else { } else {
@@ -517,6 +469,12 @@ export default {
min-width: 0; min-width: 0;
} }
.div-zoom {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.batch-operations { .batch-operations {
margin-top: 10px; margin-top: 10px;
} }
@@ -537,4 +495,8 @@ export default {
color: #808695; color: #808695;
flex-shrink: 0; flex-shrink: 0;
} }
.mt_10 {
margin-top: 10px;
}
</style> </style>

View File

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

View File

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

View File

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

View File

@@ -1,321 +1,249 @@
<template> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Row class="operation padding-row"> <div class="operation padding-row" style="margin-bottom: 10px">
<Button @click="add" type="primary">添加</Button> <el-button type="primary" @click="add">添加</el-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
>
</div> </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> </div>
</template> </template>
<script> <script>
import { import {
getLogisticsPage, getLogisticsPage,
updateLogistics, updateLogistics,
addLogistics, addLogistics,
delLogistics, delLogistics,
} from "@/api/logistics"; } from "@/api/logistics";
export default { const defaultForm = () => ({
name: "logistics", name: "",
data() { code: "",
return { standBy: false,
loading: true, // 表单加载状态 disabled: "CLOSE",
modalVisible: false, // 添加或编辑显示 });
modalTitle: "", // 添加或编辑标题
searchForm: { export default {
// 搜索框初始化对象 name: "logistics",
pageNumber: 1, // 当前页数 data() {
pageSize: 20, // 页面大小 return {
sort: "createTime", // 默认排序字段 loading: true,
order: "desc", // 默认排序方式 modalVisible: false,
name: "", modalTitle: "",
}, searchForm: {
form: { pageNumber: 1,
// 添加或编辑表单对象初始化数据 pageSize: 20,
name: "", sort: "createTime",
disabled:"CLOSE" order: "desc",
}, name: "",
// 表单验证规则 },
formValidate: { form: defaultForm(),
name: [ formValidate: {
{ name: [
required: true,
message: "请输入物流公司名称",
trigger: "blur",
},
],
},
submitLoading: false, // 添加或编辑提交状态
columns: [
{ {
title: "物流公司名称", required: true,
key: "name", message: "请输入物流公司名称",
minWidth: 120, trigger: "blur",
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();
}, },
methods: { changePage() {
// 初始化 this.getDataList();
init() { },
this.getDataList(); changePageSize() {
}, this.searchForm.pageNumber = 1;
// 分页 改变页码 this.getDataList();
changePage(v) { },
this.searchForm.pageNumber = v; getDataList() {
this.getDataList(); this.loading = true;
}, getLogisticsPage(this.searchForm).then((res) => {
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
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;
});
this.data = data;
console.log(data)
this.total = res.result.total;
}
});
this.total = this.data.length;
this.loading = false; this.loading = false;
}, if (res.success) {
// switch 切换状态 const data = res.result.records;
changeSwitch (v) { data.forEach((e) => {
this.form.name = v.name; e.switch = e.disabled === "OPEN";
this.form.code = v.code; e.standBy = e.standBy == "null" || !e.standBy ? false : !!e.standBy;
this.form.standBy = v.standBy; });
this.data = data;
this.total = res.result.total;
}
});
},
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) return;
this.submitLoading = true;
this.form.disabled = v.disabled === 'CLOSE' ? 'OPEN' : 'CLOSE'; if (this.modalTitle === "添加") {
updateLogistics(v.id, this.form).then((res) => { const payload = { ...this.form };
if (res.success) { delete payload.id;
this.$Message.success("操作成功"); addLogistics(payload).then((res) => {
this.getDataList(); this.submitLoading = false;
} if (res.success) {
}); this.$Message.success("操作成功");
}, this.getDataList();
// 确认提交 this.modalVisible = false;
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
if (this.modalTitle == "添加") {
// 添加 避免编辑后传入id等数据 记得删除
delete this.form.id;
addLogistics(this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("操作成功");
this.getDataList();
this.modalVisible = false;
}
});
} else {
// 编辑
updateLogistics(this.id, this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("操作成功");
this.getDataList();
this.modalVisible = false;
}
});
} }
} });
}); } else {
}, updateLogistics(this.id, this.form).then((res) => {
// 添加信息 this.submitLoading = false;
add() { if (res.success) {
this.modalTitle = "添加"; this.$Message.success("操作成功");
this.form = {}; this.getDataList();
this.$refs.form.resetFields(); this.modalVisible = false;
}
this.modalVisible = true; });
}, }
// 编辑 });
detail(v) {
this.id = v.id;
this.modalTitle = "修改";
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();
if (res.success) {
this.$Message.success("操作成功");
this.getDataList();
}
});
},
});
},
}, },
mounted() { add() {
this.init(); this.modalTitle = "添加";
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;
},
remove(v) {
this.$Modal.confirm({
title: "确认删除",
content: "您确认要删除 " + v.name + " ?",
onOk: () => {
return delLogistics(v.id).then((res) => {
if (res.success) {
this.$Message.success("操作成功");
this.getDataList();
}
});
},
});
},
},
mounted() {
this.init();
},
};
</script> </script>

View File

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

View File

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

View File

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

View File

@@ -1,81 +1,98 @@
<template> <template>
<div class="message-con"> <div class="message-con">
<Dropdown trigger="click"> <el-dropdown trigger="click" popper-class="todo-dropdown" @command="navigateTo">
<a href="javascript:void(0)" class="message-link">
<a href="javascript:void(0)">
{{ value > 0 ? "有" + value + "条待办事项" : "无待办事项" }} {{ value > 0 ? "有" + value + "条待办事项" : "无待办事项" }}
<Icon v-if="value!=0" type="ios-arrow-down"></Icon> <el-icon v-if="value != 0"><ArrowDown /></el-icon>
</a> </a>
<DropdownMenu v-if="value!=0" slot="list"> <template #dropdown>
<DropdownItem v-if="res.balanceCash" @click.native="navigateTo('deposit')"> <el-dropdown-menu v-if="value != 0">
<Badge :count="res.balanceCash">待处理预存款提现申请 </Badge> <el-dropdown-item v-if="res.balanceCash" command="deposit">
</DropdownItem> <el-badge :value="res.balanceCash">待处理预存款提现申请</el-badge>
<DropdownItem v-if="res.complain" @click.native="navigateTo('orderComplaint')"> </el-dropdown-item>
<Badge :count="res.complain">待处理投诉审核 </Badge> <el-dropdown-item v-if="res.complain" command="orderComplaint">
</DropdownItem> <el-badge :value="res.complain">待处理投诉审核</el-badge>
<DropdownItem v-if="res.distributionCash" @click.native="navigateTo('distributionCash')"> </el-dropdown-item>
<Badge :count="res.distributionCash">待处理分销商提现申请 </Badge> <el-dropdown-item v-if="res.distributionCash" command="distributionCash">
</DropdownItem> <el-badge :value="res.distributionCash">待处理分销商提现申请</el-badge>
<DropdownItem v-if="res.goods" @click.native="navigateTo('applyGoods')"> </el-dropdown-item>
<Badge :count="res.goods">待处理商品审核 </Badge> <el-dropdown-item v-if="res.goods" command="applyGoods">
</DropdownItem> <el-badge :value="res.goods">待处理商品审核</el-badge>
<DropdownItem v-if="res.refund" @click.native="navigateTo('afterSaleOrder')"> </el-dropdown-item>
<Badge :count="res.refund">待处理售后申请 </Badge> <el-dropdown-item v-if="res.refund" command="afterSaleOrder">
</DropdownItem> <el-badge :value="res.refund">待处理售后申请</el-badge>
<DropdownItem v-if="res.store" @click.native="navigateTo('shopAuth')"> </el-dropdown-item>
<Badge :count="res.store">待处理店铺入驻审核 </Badge> <el-dropdown-item v-if="res.store" command="shopAuth">
</DropdownItem> <el-badge :value="res.store">待处理店铺入驻审核</el-badge>
<DropdownItem v-if="res.waitPayBill" @click.native="navigateTo('accountStatementBill')"> </el-dropdown-item>
<Badge :count="res.waitPayBill">待与商家对账</Badge> <el-dropdown-item v-if="res.waitPayBill" command="accountStatementBill">
</DropdownItem> <el-badge :value="res.waitPayBill">待与商家对账</el-badge>
<div></div> </el-dropdown-item>
</DropdownMenu> </el-dropdown-menu>
</Dropdown> </template>
</el-dropdown>
</div> </div>
</template> </template>
<script> <script>
import { ArrowDown } from "@element-plus/icons-vue";
export default { export default {
name: "messageTip", name: "messageTip",
data() { components: { ArrowDown },
return {
value: 0, // 消息数量
empty: false, // 是否为空
};
},
props: { props: {
res: { res: {
type: null, type: Object,
default: () => ({}),
}, },
}, },
mounted() { computed: {
this.init(); value() {
let count = 0;
const r = this.res || {};
Object.keys(r).forEach((k) => {
if (r[k]) count += r[k];
});
return count;
},
}, },
methods: { methods: {
navigateTo(name) { navigateTo(name) {
this.$router.push({ this.$router.push({ name });
name,
});
},
init() {
Object.keys(this.res).forEach((item) => {
this.value = parseInt(this.value) + parseInt(this.res[item]);
});
}, },
}, },
}; };
</script> </script>
<style scoped lang="scss">
::v-deep .ivu-select-dropdown { <style scoped>
text-align: left; .message-link {
} color: inherit;
.message-con { text-decoration: none;
margin-right: 10px; display: inline-flex;
} align-items: center;
::v-deep .ivu-dropdown-item{ gap: 4px;
padding: 7px 20px !important;
}
::v-deep .ivu-badge-count{
right: -10px !important;
} }
</style> </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> <template>
<div class="ivu-shrinkable-menu"> <div class="shrinkable-menu">
<!-- 一级菜单 --> <el-menu
<Menu ref="sideMenu" width="80px" theme="dark" :active-name="currNav" @on-select="selectNav"> class="nav-menu-dark"
<MenuItem v-for="(item, i) in navList" :key="i" :name="item.name"> :default-active="currNav"
{{item.title}} @select="selectNav"
</MenuItem>
</Menu>
<!-- 二级菜单 -->
<Menu
ref="childrenMenu"
:active-name="$route.name"
width="120px"
@on-select="changeMenu"
> >
<template v-for="item in menuList"> <el-menu-item v-for="(item, i) in navList" :key="i" :index="item.name">
<MenuGroup :title="item.title" :key="item.id" style="padding-left:0;"> {{ item.title }}
<MenuItem :name="menu.name" v-for="menu in item.children" :key="menu.name"> </el-menu-item>
{{menu.title}} </el-menu>
</MenuItem> <el-menu
</MenuGroup> :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"
>
{{ menu.title }}
</el-menu-item>
</el-menu-item-group>
</template> </template>
</Menu> </el-menu>
</div> </div>
</template> </template>
@@ -39,60 +44,80 @@ export default {
}, },
currNav() { currNav() {
return this.$store.state.app.currNav; return this.$store.state.app.currNav;
} },
}, },
watch: { watch: {
// 监听路由变化 $route(val) {
$route: { if (
handler: function (val, oldVal) { val.meta.firstRouterName &&
if (val.meta.firstRouterName && val.meta.firstRouterName !== this.currNav) { val.meta.firstRouterName !== this.currNav
this.selectNav(val.meta.firstRouterName) ) {
} this.selectNav(val.meta.firstRouterName);
} }
} },
}, },
methods: { methods: {
changeMenu(name) { //二级路由点击 changeMenu(name) {
this.$router.push({ if (!name) return;
name: name 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.$store.commit("setCurrNav", name);
this.setStore("currNav", name); this.setStore("currNav", name);
util.initRouter(this); util.initRouter(this);
this.$nextTick(()=>{
this.$refs.childrenMenu.updateActiveName()
})
}, },
} },
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.ivu-shrinkable-menu{ .shrinkable-menu {
height: calc(100% - 60px); height: 100%;
width: 200px; width: 200px;
display: flex; display: flex;
} }
.ivu-btn-text:hover { .nav-menu-dark {
background-color: rgba(255,255,255,.2) !important; 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;
}
:deep(.el-menu-item.is-active) {
background-color: #fff !important;
color: $theme_color !important;
}
} }
.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; .sub-menu {
&:hover{ width: 120px;
background-color: #fff;
}
}
.ivu-menu-vertical{
overflow-y: auto; overflow-y: auto;
} border-right: none;
.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; :deep(.el-menu-item-group__title) {
} padding-left: 16px;
::v-deep.ivu-menu-vertical .ivu-menu-item-group-title {
height: 40px;
line-height: 40px; line-height: 40px;
padding-left: 20px; }
} }
</style> </style>

View File

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

View File

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

View File

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

View File

@@ -1,132 +1,148 @@
<template> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Row @keydown.enter.native="handleSearch"> <el-form
<Form ref="searchForm" :model="searchForm" inline :label-width="70" class="search-form"> ref="searchForm"
<Form-item label="会员名称" prop="memberName"> :model="searchForm"
<Input type="text" v-model="searchForm.memberName" placeholder="请输入会员名称" clearable style="width: 240px" /> inline
</Form-item> label-width="70px"
<Form-item label="支付时间"> class="search-form"
<DatePicker v-model="selectDate" type="datetimerange" format="yyyy-MM-dd HH:mm:ss" clearable @on-change="selectDateRange" placeholder="选择起始时间" style="width: 240px"></DatePicker> @keyup.enter="handleSearch"
</Form-item> >
<Button @click="handleSearch" type="primary" icon="ios-search" class="search-btn">搜索</Button> <el-form-item label="会员名称" prop="memberName">
</Form> <el-input
</Row> v-model="searchForm.memberName"
</Card> placeholder="请输入会员名称"
<Card> clearable
<Table class="mt_10" :loading="loading" border :columns="columns" :data="data" ref="table"></Table> style="width: 240px"
<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]" </el-form-item>
size="small" show-total show-elevator show-sizer></Page> <el-form-item label="支付时间">
</Row> <el-date-picker
</Card> 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> </div>
</template> </template>
<script> <script>
import { getUserWallet } from "@/api/member"; import { getUserWallet } from "@/api/member";
export default { export default {
name: "walletLog", name: "walletLog",
data() { data() {
return { return {
loading: true, // 表单加载状态 loading: true,
searchForm: { searchForm: {
// 搜索框初始化对象 pageNumber: 1,
pageNumber: 1, // 当前页数 pageSize: 20,
pageSize: 20, // 页面大小 sort: "createTime",
sort: "createTime", // 默认排序字段 order: "desc",
order: "desc", // 默认排序方式 startDate: "",
startDate: "", // 起始时间 endDate: "",
endDate: "", // 终止时间 memberName: "",
memberName: "", },
}, selectDate: null,
selectDate: null, // 选择时间段 data: [],
columns: [ total: 0,
// 表头
{
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, // 表单数据总数
}; };
}, },
methods: { 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() { init() {
this.getDataList(); this.getDataList();
}, },
// 分页 改变页码
changePage(v) { changePage(v) {
this.searchForm.pageNumber = v; this.searchForm.pageNumber = v;
this.getDataList(); this.getDataList();
}, },
// 分页 改变页数
changePageSize(v) { changePageSize(v) {
this.searchForm.pageNumber = 1; this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v; this.searchForm.pageSize = v;
this.getDataList(); this.getDataList();
}, },
// 搜索
handleSearch() { handleSearch() {
this.searchForm.pageNumber = 1; this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20; this.searchForm.pageSize = 20;
this.getDataList(); this.getDataList();
}, },
// 时间段赋值
selectDateRange(v) { selectDateRange(v) {
if (v) { if (v && v.length === 2) {
this.searchForm.startDate = v[0]; this.searchForm.startDate = v[0];
this.searchForm.endDate = v[1]; this.searchForm.endDate = v[1];
} else {
this.searchForm.startDate = "";
this.searchForm.endDate = "";
} }
}, },
// 获取列表数据
getDataList() { getDataList() {
this.loading = true;
getUserWallet(this.searchForm).then((res) => { getUserWallet(this.searchForm).then((res) => {
this.loading = false; this.loading = false;
if (res.success) { if (res.success) {
@@ -134,8 +150,6 @@ export default {
this.total = res.result.total; this.total = res.result.total;
} }
}); });
this.total = this.data.length;
this.loading = false;
}, },
}, },
mounted() { mounted() {

View File

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

View File

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

View File

@@ -1,57 +1,80 @@
<template> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Form <el-form
ref="searchForm" ref="searchForm"
:model="searchForm" :model="searchForm"
inline inline
:label-width="90" label-width="90px"
@keydown.enter.native="handleSearch"
@submit.native.prevent
class="search-form" class="search-form"
@keyup.enter="handleSearch"
> >
<FormItem label="客户手机号" prop="mobile"> <el-form-item label="客户手机号" prop="mobile">
<Input <el-input
v-model="searchForm.memberMobile" v-model="searchForm.memberMobile"
clearable clearable
placeholder="请输入客户手机号" placeholder="请输入客户手机号"
style="width: 220px" style="width: 220px"
/> />
</FormItem> </el-form-item>
<FormItem label="规则" prop="ruleKey"> <el-form-item label="规则" prop="ruleKey">
<Select v-model="searchForm.ruleKey" clearable filterable style="width: 220px"> <el-select v-model="searchForm.ruleKey" clearable filterable style="width: 220px">
<Option v-for="item in ruleOptions" :key="item.value" :value="item.value"> <el-option
{{ item.label }} v-for="item in ruleOptions"
</Option> :key="item.value"
</Select> :label="item.label"
</FormItem> :value="item.value"
<Button type="primary" icon="ios-search" @click="handleSearch">搜索</Button> />
</Form> </el-select>
</Card> </el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<Card> <el-card>
<Table <el-table v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
:loading="loading" <el-table-column label="客户手机号" width="140" show-overflow-tooltip>
border <template #default="{ row }">
:columns="columns" <span v-if="row">{{ row.memberMobile || row.mobile || "-" }}</span>
:data="data" </template>
class="mt_10" </el-table-column>
></Table> <el-table-column label="规则名称" width="150" show-overflow-tooltip>
<Row type="flex" justify="end" class="mt_10"> <template #default="{ row }">
<Page <span v-if="row">{{ row.ruleName || findRuleName(row.ruleKey) || "-" }}</span>
:current="searchForm.pageNumber" </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" :total="total"
:page-size="searchForm.pageSize" layout="total, sizes, prev, pager, next, jumper"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small" size="small"
show-total @current-change="changePage"
show-elevator @size-change="changePageSize"
show-sizer />
></Page> </div>
</Row> </el-card>
</Card>
</div> </div>
</template> </template>
@@ -86,67 +109,20 @@ export default {
memberMobile: "", memberMobile: "",
ruleKey: "", 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() { mounted() {
this.init(); this.init();
}, },
methods: { 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) { findRuleName(ruleKey) {
const hit = this.ruleOptions.find((item) => item.value === ruleKey); const hit = this.ruleOptions.find((item) => item.value === ruleKey);
return hit ? hit.label : ""; return hit ? hit.label : "";

View File

@@ -1,10 +1,226 @@
<template> <template>
<div class="experience-setting"> <div class="experience-setting">
<Card> <el-card>
<Form :label-width="120" label-position="right"> <el-form label-width="120px" label-position="right">
<Table :loading="loading" :columns="columns" :data="form.items" class="mt_10 experience-table"></Table> <el-table
<FormItem label="经验值说明" style="margin-top: 16px" class="desc-item"> v-loading="loading"
<Input :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" v-model="form.description"
type="textarea" type="textarea"
:rows="4" :rows="4"
@@ -12,12 +228,12 @@
show-word-limit show-word-limit
placeholder="请输入经验值说明" placeholder="请输入经验值说明"
/> />
</FormItem> </el-form-item>
<FormItem> <el-form-item>
<Button type="primary" :loading="submitLoading" @click="submit">保存</Button> <el-button type="primary" :loading="submitLoading" @click="submit">保存</el-button>
</FormItem> </el-form-item>
</Form> </el-form>
</Card> </el-card>
</div> </div>
</template> </template>
@@ -58,340 +274,33 @@ export default {
loading: false, loading: false,
submitLoading: false, submitLoading: false,
form: defaultForm(), 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() { mounted() {
this.loadData(); this.loadData();
}, },
methods: { methods: {
formatInputValue(v) {
return v == null ? "" : String(v);
},
getRawInputValue(v) { getRawInputValue(v) {
return v && v.target ? v.target.value : v; return v && v.target ? v.target.value : v;
}, },
updateRuleEnabled(index, enabled) { updateRuleEnabled(index, enabled) {
const item = this.form.items[index] || {}; const item = this.form.items[index] || {};
this.$set(this.form.items, index, { this.form.items[index] = {
...item, ...item,
enabled: !!enabled, enabled: !!enabled,
}); };
}, },
updateRuleValue(index, v) { updateRuleValue(index, v) {
const raw = this.getRawInputValue(v); const raw = this.getRawInputValue(v);
const next = Number(raw); const next = Number(raw);
const item = this.form.items[index] || {}; const item = this.form.items[index] || {};
this.$set(this.form.items, index, { this.form.items[index] = {
...item, ...item,
value: Number.isFinite(next) ? next : null, value: Number.isFinite(next) ? next : null,
}); };
}, },
commitRuleValue(index) { commitRuleValue(index) {
const item = this.form.items[index] || {}; const item = this.form.items[index] || {};
@@ -402,41 +311,41 @@ export default {
else if (next > 100) value = 100; else if (next > 100) value = 100;
else value = Math.floor(next); else value = Math.floor(next);
} }
this.$set(this.form.items, index, { this.form.items[index] = {
...item, ...item,
value, value,
}); };
}, },
updateRuleMaxValue(index, v) { updateRuleMaxValue(index, v) {
const raw = this.getRawInputValue(v); const raw = this.getRawInputValue(v);
const item = this.form.items[index] || {}; const item = this.form.items[index] || {};
if (raw == null || raw === "") { if (raw == null || raw === "") {
this.$set(this.form.items, index, { this.form.items[index] = {
...item, ...item,
maxValue: null, maxValue: null,
}); };
return; return;
} }
const next = Number(raw); const next = Number(raw);
this.$set(this.form.items, index, { this.form.items[index] = {
...item, ...item,
maxValue: Number.isFinite(next) ? next : null, maxValue: Number.isFinite(next) ? next : null,
}); };
}, },
commitRuleMaxValue(index) { commitRuleMaxValue(index) {
const item = this.form.items[index] || {}; const item = this.form.items[index] || {};
if (item.maxValue == null || item.maxValue === "") { if (item.maxValue == null || item.maxValue === "") {
this.$set(this.form.items, index, { this.form.items[index] = {
...item, ...item,
maxValue: null, maxValue: null,
}); };
return; return;
} }
const next = Number(item.maxValue); const next = Number(item.maxValue);
this.$set(this.form.items, index, { this.form.items[index] = {
...item, ...item,
maxValue: Number.isFinite(next) && next >= 1 ? Math.floor(next) : null, maxValue: Number.isFinite(next) && next >= 1 ? Math.floor(next) : null,
}); };
}, },
normalizeConfig(val) { normalizeConfig(val) {
if (!val) return {}; if (!val) return {};
@@ -535,21 +444,52 @@ export default {
padding: 2px 0; 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; background: #fafbfc;
} }
::v-deep .experience-table .ivu-table td { :deep(.experience-table .el-table__body td) {
padding-top: 12px; padding-top: 12px;
padding-bottom: 12px; padding-bottom: 12px;
} }
::v-deep .experience-table .ivu-table-cell { :deep(.experience-table .cell) {
font-size: 13px; font-size: 13px;
line-height: 1.7; line-height: 1.7;
} }
::v-deep .desc-item .ivu-form-item-label { :deep(.desc-item .el-form-item__label) {
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
} }

View File

@@ -1,120 +1,180 @@
<template> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Row class="operation padding-row"> <div class="operation padding-row">
<Button type="primary" @click="openAdd">添加客户等级</Button> <el-button type="primary" @click="openAdd">添加客户等级</el-button>
</Row> </div>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
class="mt_10"
></Table>
</Card>
<Modal v-model="addFlag" title="添加客户等级" width="720" :z-index="950" :mask-closable="false"> <el-table v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
<Form ref="addForm" :model="formAdd" :rules="rules" :label-width="110"> <el-table-column prop="gradeName" label="等级名称" width="130" show-overflow-tooltip />
<FormItem label="等级名称" prop="gradeName"> <el-table-column label="默认等级" width="95">
<Input v-model="formAdd.gradeName" maxlength="50" placeholder="请输入等级名称" /> <template #default="{ row }">
</FormItem> <el-tag v-if="row" :type="row.isDefault === true ? 'success' : 'info'">
<FormItem label="是否默认" prop="isDefault"> {{ row.isDefault === true ? "" : "" }}
<RadioGroup v-model="formAdd.isDefault"> </el-tag>
<Radio :label="true"></Radio> </template>
<Radio :label="false"></Radio> </el-table-column>
</RadioGroup> <el-table-column label="等级图标" width="100">
</FormItem> <template #default="{ row }">
<FormItem label="等级图标" prop="gradeImage"> <span v-if="row && !row.gradeImage">-</span>
<upload-pic-input v-model="formAdd.gradeImage"></upload-pic-input> <img
</FormItem> v-else-if="row"
<FormItem label="等级背景图" prop="gradeBackground"> :src="row.gradeImage"
<upload-pic-input v-model="formAdd.gradeBackground"></upload-pic-input> alt="等级图标"
</FormItem> style="width: 48px; height: 48px; object-fit: contain; border: 1px solid #dcdee2; border-radius: 4px; background: #fff"
<FormItem label="字体颜色" prop="gradeFontColor"> />
<Input v-model="formAdd.gradeFontColor" maxlength="20" placeholder="如:#333333" /> </template>
</FormItem> </el-table-column>
<FormItem label="所需经验值" prop="requiredExperience"> <el-table-column label="等级背景图" width="120">
<InputNumber v-model="formAdd.requiredExperience" :min="1" :precision="0" style="width: 220px"></InputNumber> <template #default="{ row }">
</FormItem> <span v-if="row && !row.gradeBackground">-</span>
<FormItem label="等级排序" prop="gradeSort"> <img
<InputNumber v-model="formAdd.gradeSort" :min="1" :max="9999" :precision="0" style="width: 220px"></InputNumber> v-else-if="row"
</FormItem> :src="row.gradeBackground"
<FormItem label="等级开关" prop="gradeState"> alt="等级背景图"
<RadioGroup v-model="formAdd.gradeState"> style="width: 64px; height: 40px; object-fit: cover; border: 1px solid #dcdee2; border-radius: 4px; background: #fff"
<Radio label="OPEN">开启</Radio> />
<Radio label="CLOSE">关闭</Radio> </template>
</RadioGroup> </el-table-column>
</FormItem> <el-table-column prop="requiredExperience" label="所需经验值" width="110" />
<FormItem label="关联权益" prop="benefitIds"> <el-table-column prop="gradeSort" label="等级排序" width="95" />
<Select <el-table-column label="状态" width="118" align="center">
:value="addBenefitOrder" <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 multiple
filterable filterable
placeholder="请选择客户权益" placeholder="请选择客户权益"
style="width: 100%" style="width: 100%"
@on-change="onAddBenefitIdsChange" @change="onAddBenefitIdsChange"
> >
<Option v-for="b in benefitOptions" :key="b.id" :value="String(b.id)">{{ benefitOptionLabel(b) }}</Option> <el-option
</Select> v-for="b in benefitOptions"
</FormItem> :key="b.id"
</Form> :value="String(b.id)"
<div slot="footer"> :label="benefitOptionLabel(b)"
<Button @click="addFlag = false">取消</Button> />
<Button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</Button> </el-select>
</div> </el-form-item>
</Modal> </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"> <el-dialog v-model="editFlag" title="编辑客户等级" width="720px" :z-index="950" :close-on-click-modal="false" destroy-on-close>
<Form ref="editForm" :model="formEdit" :rules="rules" :label-width="110"> <el-form ref="editForm" :model="formEdit" :rules="rules" label-width="110px">
<Input v-model="formEdit.id" v-show="false" /> <el-input v-model="formEdit.id" style="display: none" />
<FormItem label="等级名称" prop="gradeName"> <el-form-item label="等级名称" prop="gradeName">
<Input v-model="formEdit.gradeName" maxlength="50" placeholder="请输入等级名称" /> <el-input v-model="formEdit.gradeName" maxlength="50" placeholder="请输入等级名称" />
</FormItem> </el-form-item>
<FormItem label="是否默认" prop="isDefault"> <el-form-item label="是否默认" prop="isDefault">
<RadioGroup v-model="formEdit.isDefault"> <el-radio-group v-model="formEdit.isDefault">
<Radio :label="true"></Radio> <el-radio :value="true"></el-radio>
<Radio :label="false"></Radio> <el-radio :value="false"></el-radio>
</RadioGroup> </el-radio-group>
</FormItem> </el-form-item>
<FormItem label="等级图标" prop="gradeImage"> <el-form-item label="等级图标" prop="gradeImage">
<upload-pic-input v-model="formEdit.gradeImage"></upload-pic-input> <upload-pic-input v-model="formEdit.gradeImage" />
</FormItem> </el-form-item>
<FormItem label="等级背景图" prop="gradeBackground"> <el-form-item label="等级背景图" prop="gradeBackground">
<upload-pic-input v-model="formEdit.gradeBackground"></upload-pic-input> <upload-pic-input v-model="formEdit.gradeBackground" />
</FormItem> </el-form-item>
<FormItem label="字体颜色" prop="gradeFontColor"> <el-form-item label="字体颜色" prop="gradeFontColor">
<Input v-model="formEdit.gradeFontColor" maxlength="20" placeholder="如:#333333" /> <el-input v-model="formEdit.gradeFontColor" maxlength="20" placeholder="如:#333333" />
</FormItem> </el-form-item>
<FormItem label="所需经验值" prop="requiredExperience"> <el-form-item label="所需经验值" prop="requiredExperience">
<InputNumber v-model="formEdit.requiredExperience" :min="1" :precision="0" style="width: 220px"></InputNumber> <el-input-number v-model="formEdit.requiredExperience" :min="1" :precision="0" style="width: 220px" />
</FormItem> </el-form-item>
<FormItem label="等级排序" prop="gradeSort"> <el-form-item label="等级排序" prop="gradeSort">
<InputNumber v-model="formEdit.gradeSort" :min="1" :max="9999" :precision="0" style="width: 220px"></InputNumber> <el-input-number v-model="formEdit.gradeSort" :min="1" :max="9999" :precision="0" style="width: 220px" />
</FormItem> </el-form-item>
<FormItem label="等级开关" prop="gradeState"> <el-form-item label="等级开关" prop="gradeState">
<RadioGroup v-model="formEdit.gradeState"> <el-radio-group v-model="formEdit.gradeState">
<Radio label="OPEN">开启</Radio> <el-radio value="OPEN">开启</el-radio>
<Radio label="CLOSE">关闭</Radio> <el-radio value="CLOSE">关闭</el-radio>
</RadioGroup> </el-radio-group>
</FormItem> </el-form-item>
<FormItem label="关联权益" prop="benefitIds"> <el-form-item label="关联权益" prop="benefitIds">
<Select <el-select
:value="editBenefitOrder" :model-value="editBenefitOrder"
multiple multiple
filterable filterable
placeholder="请选择客户权益" placeholder="请选择客户权益"
style="width: 100%" style="width: 100%"
@on-change="onEditBenefitIdsChange" @change="onEditBenefitIdsChange"
> >
<Option v-for="b in benefitOptions" :key="b.id" :value="String(b.id)">{{ benefitOptionLabel(b) }}</Option> <el-option
</Select> v-for="b in benefitOptions"
</FormItem> :key="b.id"
</Form> :value="String(b.id)"
<div slot="footer"> :label="benefitOptionLabel(b)"
<Button @click="editFlag = false">取消</Button> />
<Button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</Button> </el-select>
</div> </el-form-item>
</Modal> </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> </div>
</template> </template>
@@ -135,7 +195,6 @@ const buildDefaultForm = () => ({
benefitIds: "", benefitIds: "",
}); });
/** 权益多选:新勾选追加到末尾,取消勾选移除,保持已有顺序 */
function syncOrderedBenefitIds(prevOrder, selected) { function syncOrderedBenefitIds(prevOrder, selected) {
const sel = Array.isArray(selected) ? selected.map((id) => String(id)) : []; const sel = Array.isArray(selected) ? selected.map((id) => String(id)) : [];
const out = []; const out = [];
@@ -157,144 +216,6 @@ export default {
data() { data() {
return { return {
loading: true, 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: [], data: [],
addFlag: false, addFlag: false,
editFlag: false, editFlag: false,
@@ -302,7 +223,6 @@ export default {
submitEditLoading: false, submitEditLoading: false,
formAdd: buildDefaultForm(), formAdd: buildDefaultForm(),
formEdit: buildDefaultForm(), formEdit: buildDefaultForm(),
/** 关联权益 id 顺序(与 benefitIds 一致) */
addBenefitOrder: [], addBenefitOrder: [],
editBenefitOrder: [], editBenefitOrder: [],
benefitOptions: [], benefitOptions: [],
@@ -506,19 +426,19 @@ export default {
title: "提示", title: "提示",
content: `<p>确定${text}该客户等级?</p>`, content: `<p>确定${text}该客户等级?</p>`,
onOk: () => { onOk: () => {
this.$set(row, "_gradeStateLoading", true); row._gradeStateLoading = true;
return API_Member.updateMemberGradeState(row.id, nextState) return API_Member.updateMemberGradeState(row.id, nextState)
.then((res) => { .then((res) => {
this.$set(row, "_gradeStateLoading", false); row._gradeStateLoading = false;
if (res && res.success) { if (res && res.success) {
this.$Message.success(`${text}成功`); this.$Message.success(`${text}成功`);
this.$set(row, "gradeState", nextState); row.gradeState = nextState;
} else { } else {
this.getData(); this.getData();
} }
}) })
.catch(() => { .catch(() => {
this.$set(row, "_gradeStateLoading", false); row._gradeStateLoading = false;
}); });
}, },
}); });
@@ -545,3 +465,15 @@ export default {
}, },
}; };
</script> </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> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Row class="operation padding-row"> <div class="operation padding-row">
<Button type="primary" @click="openAdd">添加分组</Button> <el-button type="primary" @click="openAdd">添加分组</el-button>
</Row> </div>
<Table </el-card>
:loading="loading"
border <el-card>
:columns="columns" <el-table ref="table" v-loading="loading" :data="data" border class="mt_10" style="width: 100%">
:data="data" <el-table-column prop="groupName" label="分组名称" min-width="160" show-overflow-tooltip />
ref="table" <el-table-column prop="description" label="分组描述" min-width="240" show-overflow-tooltip />
class="mt_10" <el-table-column prop="createTime" label="创建时间" min-width="180" />
></Table> <el-table-column prop="updateTime" label="更新时间" min-width="180" />
<Row type="flex" justify="end" class="mt_10"> <el-table-column label="操作" width="200" align="center" fixed="right">
<Page <template #default="{ row }">
:current="searchForm.pageNumber" <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" :total="total"
:page-size="searchForm.pageSize" layout="total, sizes, prev, pager, next, jumper"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small" size="small"
show-total @current-change="changePage"
show-elevator @size-change="changePageSize"
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>
</div> </div>
</Modal> </el-card>
<Modal v-model="editFlag" title="编辑分组">
<Form ref="editForm" :model="formEdit" :rules="rulesEdit" :label-width="90"> <el-dialog v-model="addFlag" title="添加分组" width="500px" :close-on-click-modal="false" destroy-on-close>
<Input v-model="formEdit.id" v-show="false" /> <el-form ref="addForm" :model="formAdd" :rules="rulesAdd" label-width="90px">
<FormItem label="分组名称" prop="groupName" style="width: 90%;"> <el-form-item label="分组名称" prop="groupName" style="width: 90%">
<Input v-model="formEdit.groupName" maxlength="30" placeholder="请输入分组名称" /> <el-input v-model="formAdd.groupName" maxlength="30" placeholder="请输入分组名称" />
</FormItem> </el-form-item>
<FormItem label="分组描述" prop="description" style="width: 90%;"> <el-form-item label="分组描述" prop="description" style="width: 90%">
<Input v-model="formEdit.description" maxlength="200" placeholder="请输入分组描述" /> <el-input v-model="formAdd.description" maxlength="200" placeholder="请输入分组描述" />
</FormItem> </el-form-item>
</Form> </el-form>
<div slot="footer"> <template #footer>
<Button @click="editFlag = false">取消</Button> <el-button @click="addFlag = false">取消</el-button>
<Button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</Button> <el-button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</el-button>
</div> </template>
</Modal> </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> </div>
</template> </template>
@@ -71,67 +82,6 @@ export default {
pageNumber: 1, pageNumber: 1,
pageSize: 20, 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: [], data: [],
total: 0, total: 0,
addFlag: false, addFlag: false,
@@ -148,10 +98,10 @@ export default {
description: "", description: "",
}, },
rulesAdd: { rulesAdd: {
groupName: [{ required: true, message: "请输入分组名称" }], groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
}, },
rulesEdit: { rulesEdit: {
groupName: [{ required: true, message: "请输入分组名称" }], groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
}, },
}; };
}, },
@@ -236,7 +186,7 @@ export default {
remove(row) { remove(row) {
this.$Modal.confirm({ this.$Modal.confirm({
title: "提示", title: "提示",
content: "<p>确定删除该分组?</p>", content: "确定删除该分组?",
onOk: () => { onOk: () => {
API_Member.deleteMemberGroup(row.id).then((res) => { API_Member.deleteMemberGroup(row.id).then((res) => {
if (res && res.success) { if (res && res.success) {
@@ -255,3 +205,16 @@ export default {
}, },
}; };
</script> </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> <template>
<div class="search"> <div class="search">
<Row> <el-card>
<Card> <el-form
<Row @keydown.enter.native="handleSearch"> ref="searchForm"
<Form :model="searchForm"
ref="searchForm" inline
:model="searchForm" label-width="70px"
inline class="search-form"
:label-width="70" @keyup.enter="handleSearch"
class="search-form"
>
<Form-item label="会员名称" prop="username">
<Input
type="text"
v-model="searchForm.username"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</Form-item>
<Form-item label="联系方式" prop="mobile">
<Input
type="text"
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>
<!-- 修改模态框 -->
<Modal
v-model="descFlag"
:title="descTitle"
@on-ok="handleSubmitModal"
width="500"
>
<Form
ref="formValidate"
:model="formValidate"
:rules="ruleValidate"
:label-width="80"
> >
<FormItem label="头像"> <el-form-item label="会员名称" prop="username">
<el-input
v-model="searchForm.username"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</el-form-item>
<el-form-item label="联系方式" prop="mobile">
<el-input
v-model="searchForm.mobile"
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="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" /> <img :src="formValidate.face" class="face" />
<Button <el-button
type="text" type="primary"
link
class="upload" class="upload"
@click=" @click="
() => { () => {
this.picModelFlag = true; picModelFlag = true;
this.$refs.ossManage.selectImage = true; $refs.ossManage.selectImage = true;
} }
" "
>修改 >修改</el-button>
</Button>
<input type="file" style="display: none" id="file" /> <input type="file" style="display: none" id="file" />
</FormItem> </el-form-item>
<FormItem label="会员名称" prop="name"> <el-form-item label="会员名称" prop="name">
<Input <el-input v-model="formValidate.username" style="width: 200px" disabled />
v-model="formValidate.username" </el-form-item>
style="width: 200px" <el-form-item label="用户昵称" prop="name">
disabled <el-input v-model="formValidate.nickName" style="width: 200px" />
/> </el-form-item>
</FormItem> <el-form-item label="性别" prop="sex">
<FormItem label="用户昵称" prop="name"> <el-radio-group v-model="formValidate.sex">
<Input v-model="formValidate.nickName" style="width: 200px" /> <el-radio-button :value="1"></el-radio-button>
</FormItem> <el-radio-button :value="0"></el-radio-button>
<FormItem label="性别" prop="sex"> </el-radio-group>
<RadioGroup </el-form-item>
type="button" <el-form-item label="修改密码" prop="password">
button-style="solid" <el-input v-model="formValidate.newPassword" type="password" show-password style="width: 220px" />
v-model="formValidate.sex" </el-form-item>
> <el-form-item label="生日" prop="birthday">
<Radio :label="1"> <el-date-picker
<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"
v-model="formValidate.birthday" v-model="formValidate.birthday"
type="date"
value-format="YYYY-MM-DD"
style="width: 220px" style="width: 220px"
></DatePicker> />
</FormItem> </el-form-item>
<FormItem label="所在地" prop="mail"> <el-form-item label="所在地" prop="mail">
{{ formValidate.region || '暂无地址' }} {{ formValidate.region || "暂无地址" }}
<Button style="margin-left: 10px;" @click="$refs.map.open()">选择</Button> <el-button style="margin-left: 10px" @click="$refs.map.open()">选择</el-button>
</FormItem> </el-form-item>
</Form> </el-form>
</Modal> <template #footer>
<Modal width="1200px" v-model="picModelFlag"> <el-button @click="descFlag = false">取消</el-button>
<ossManage @callback="callbackSelected" :isComponent="true" :initialize="picModelFlag" ref="ossManage" /> <el-button type="primary" @click="handleSubmitModal">确定</el-button>
</Modal> </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" /> <multipleMap ref="map" @callback="selectedRegion" />
</div> </div>
</template> </template>
<script> <script>
import * as API_Member from "@/api/member.js"; import * as API_Member from "@/api/member.js";
import ossManage from "@/views/sys/oss-manage/ossManage"; import ossManage from "@/views/sys/oss-manage/ossManage";
import multipleMap from "@/components/map/multiple-map"; import multipleMap from "@/components/map/multiple-map";
export default { export default {
name: "memberRecycle", name: "memberRecycle",
components: { components: {
ossManage, ossManage,
multipleMap multipleMap,
}, },
data() { data() {
return { return {
selectedMember: false, //是否是其他组件调用 selectedMember: false,
descTitle: "", // modal标题 descTitle: "",
descFlag: false, //编辑查看框 descFlag: false,
openSearch: true, // 显示搜索 openSearch: true,
loading: true, // 表单加载状态 loading: true,
searchForm: { searchForm: {
// 请求参数
pageNumber: 1, pageNumber: 1,
pageSize: 20, pageSize: 20,
order: "desc", order: "desc",
@@ -177,198 +175,72 @@ export default {
mobile: "", mobile: "",
disabled: "CLOSE", disabled: "CLOSE",
}, },
picModelFlag: false, // 选择图片 picModelFlag: false,
formValidate: {}, // 表单数据 formValidate: {},
ruleValidate: {}, //修改验证 ruleValidate: {},
columns: [ data: [],
{ total: 0,
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, // 表单数据总数
}; };
}, },
methods: { methods: {
// 回调给父级
callback(val) { callback(val) {
this.$emit("callback", val); this.$emit("callback", val);
}, },
// 初始化数据
init() { init() {
this.getData(); this.getData();
}, },
// 分页 修改页码 changePage() {
changePage(v) {
this.searchForm.pageNumber = v;
this.getData(); this.getData();
}, },
// 分页 修改页数 changePageSize() {
changePageSize(v) {
this.searchForm.pageNumber = 1; this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getData(); this.getData();
}, },
// 搜索
handleSearch() { handleSearch() {
this.searchForm.pageNumber = 1; this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20; this.searchForm.pageSize = 20;
this.getData(); this.getData();
}, },
//查看详情修改
editPerm(val) { editPerm(val) {
this.descTitle = `查看用户 ${val.username}`; this.descTitle = `查看用户 ${val.username}`;
this.descFlag = true; this.descFlag = true;
this.getMemberInfo(val.id); this.getMemberInfo(val.id);
}, },
/**
* 查询查看会员详情
*/
getMemberInfo(id) { getMemberInfo(id) {
API_Member.getMemberInfoData(id).then((res) => { API_Member.getMemberInfoData(id).then((res) => {
if (res.result) { if (res.result) {
this.$set(this, "formValidate", res.result); this.formValidate = res.result;
} }
}); });
}, },
//查询会员列表
getData() { getData() {
this.loading = true;
API_Member.getMemberListData(this.searchForm).then((res) => { API_Member.getMemberListData(this.searchForm).then((res) => {
this.loading = false;
if (res.success) { if (res.success) {
this.loading = false;
this.data = res.result.records; this.data = res.result.records;
this.total = res.result.total; this.total = res.result.total;
} }
}); });
}, },
// 选中的图片
callbackSelected(val) { callbackSelected(val) {
this.picModelFlag = false; this.picModelFlag = false;
this.formValidate.face = val.url; this.formValidate.face = val.url;
}, },
// 选中的地址
selectedRegion(val) { selectedRegion(val) {
if(val.type === 'select'){ if (val.type === "select") {
const paths = val.data.map(item => item.name).join(',') const paths = val.data.map((item) => item.name).join(",");
const ids = val.data.map(item => item.id).join(',') const ids = val.data.map((item) => item.id).join(",");
this.formValidate.region = paths;
this.$set(this.formValidate,'region',paths) this.formValidate.regionId = ids;
this.$set(this.formValidate,'regionId',ids) } else {
this.formValidate.region = val.data.addr;
} this.formValidate.regionId = val.data.addrId;
else{
this.$set(this.formValidate,'region',val.data.addr)
this.$set(this.formValidate,'regionId',val.data.addrId)
} }
}, },
//详细
detail(row) { 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) { enable(v) {
let params = { let params = {
memberIds: [v.id], memberIds: [v.id],
@@ -382,26 +254,19 @@ export default {
if (res.success) { if (res.success) {
this.$Message.success("启用成功"); this.$Message.success("启用成功");
this.getData(); this.getData();
} else {
// this.$Message.error(res.message);
} }
}); });
}, },
}); });
}, },
// 提交修改数据
handleSubmitModal() { handleSubmitModal() {
const { nickName, sex, username, face, newPassword, id ,regionId,region} = const { nickName, sex, username, face, newPassword, id, regionId, region } = this.formValidate;
this.formValidate;
let time = new Date(this.formValidate.birthday); let time = new Date(this.formValidate.birthday);
let birthday = let birthday = time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
let submit = { let submit = {
regionId:regionId, regionId: regionId,
region: region, region: region,
nickName, nickName,
sex, sex,
birthday, birthday,
face: face || "", face: face || "",
@@ -413,6 +278,7 @@ export default {
API_Member.updateMember(submit).then((res) => { API_Member.updateMember(submit).then((res) => {
if (res.result) { if (res.result) {
this.$Message.success("修改成功!"); this.$Message.success("修改成功!");
this.descFlag = false;
this.init(); this.init();
} }
}); });

View File

@@ -1,98 +1,108 @@
<template> <template>
<div> <div>
<!--微信模板--> <el-dialog v-model="wechatModal" title="微信设置" width="530px">
<Modal v-model="wechatModal" width="530"> <el-form ref="wechatFormData" :model="wechatFormData" label-width="100px">
<p slot="header"> <el-form-item v-if="tab === 'WECHAT'" label="模板名称">
<Icon type="edit"></Icon> <el-input v-model="wechatFormData.name" maxlength="9" disabled />
<span>微信设置</span> </el-form-item>
</p> <el-form-item v-if="tab === 'WECHAT'" label="头部信息" prop="first">
<div> <el-input v-model="wechatFormData.first" maxlength="50" />
<Form ref="wechatFormData" :model="wechatFormData" label-position="left" :label-width="100"> </el-form-item>
<FormItem v-if="tab === 'WECHAT'" label="模板名称"> <el-form-item v-if="tab === 'WECHAT'" label="备注" prop="remark">
<Input v-model="wechatFormData.name" size="large" maxlength="9" disabled></Input> <el-input v-model="wechatFormData.remark" type="textarea" :rows="5" maxlength="150" />
</FormItem> </el-form-item>
<FormItem v-if="tab === 'WECHAT'" label="头部信息" prop="first"> <el-form-item label="是否开启" prop="enable">
<Input v-model="wechatFormData.first" size="large" maxlength="50"></Input> <el-switch v-model="wechatFormData.enable" active-text="开启" inactive-text="关闭" />
</FormItem> </el-form-item>
<FormItem v-if="tab === 'WECHAT'" label="备注" prop="remark"> </el-form>
<Input class='textarea' :rows="5" :autosize="{maxRows:5,minRows: 5}" v-model="wechatFormData.remark" <template #footer>
type="textarea" maxlength="150"/> <el-button v-if="tab === 'WECHAT'" type="primary" @click="wechatFormDataEdit">保存</el-button>
</FormItem> <el-button v-else type="primary" @click="wechatMPFormDataEdit">保存</el-button>
<FormItem label="是否开启" prop="enable"> </template>
<i-switch v-model="wechatFormData.enable" size="large"> </el-dialog>
<span slot="open">开启</span>
<span slot="close">关闭</span>
</i-switch>
</FormItem>
</Form>
</div> <el-card>
<div slot="footer" style="text-align: right"> <el-tabs v-model="tab" @tab-click="tabPaneChange">
<Button v-if="tab === 'WECHAT'" type="primary" @click="wechatFormDataEdit">保存</Button> <el-tab-pane label="微信消息" name="WECHAT">
<div class="search">
<Button v-else type="primary" @click="wechatMPFormDataEdit">保存</Button> <div class="operation mt_10">
</div> <el-button type="primary" @click="weChatSync">初始化微信消息</el-button>
</Modal> </div>
<el-table v-loading="loading" border :data="weChatData" ref="weChatTable" style="width: 100%">
<Card> <el-table-column prop="code" label="模板编号" width="500" sortable />
<Tabs @on-click="tabPaneChange" v-model="tab"> <el-table-column label="是否开启" width="150" sortable>
<TabPane label="微信消息" name="WECHAT"> <template #default="{ row }">
<div class="search"> <span v-if="row">{{ row.enable ? "开启" : "关闭" }}</span>
<Row class="operation mt_10"> </template>
<Button @click="weChatSync" type="primary">初始化微信消息</Button> </el-table-column>
</Row> <el-table-column prop="name" label="模板名称" width="200" sortable />
<Table <el-table-column prop="createTime" label="创建时间" sortable />
:loading="loading" <el-table-column label="操作" width="200" align="center" fixed="right">
border <template #default="{ row }">
:columns="weChatColumns" <template v-if="row">
:data="weChatData" <a class="link-text" @click="wechatSettingAlert(row)">编辑</a>
ref="weChatTable" <span class="op-split">|</span>
></Table> <a class="link-text" @click="delWeChat(row)">删除</a>
<Row type="flex" justify="end" class="mt_10"> </template>
<Page </template>
:current="weChatSearchForm.pageNumber" </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" :total="weChatTotal"
:page-size="weChatSearchForm.pageSize" layout="total, sizes, prev, pager, next"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small" size="small"
></Page> @current-change="changePage"
</Row> @size-change="changePageSize"
</div> />
</TabPane> </div>
</div>
</el-tab-pane>
<TabPane label="微信小程序订阅消息" name="WECHATMP"> <el-tab-pane label="微信小程序订阅消息" name="WECHATMP">
<div class="search"> <div class="search">
<Row class="operation mt_10"> <div class="operation mt_10">
<Button @click="weChatSync('mp')" type="primary">初始化微信小程序订阅消息</Button> <el-button type="primary" @click="weChatSync('mp')">初始化微信小程序订阅消息</el-button>
</Row> </div>
<Table <el-table v-loading="loading" border :data="weChatMPData" ref="weChatMPTable" style="width: 100%">
:loading="loading" <el-table-column prop="code" label="模板编号" width="500" sortable />
border <el-table-column label="是否开启" width="150" sortable>
:columns="weChatColumns" <template #default="{ row }">
:data="weChatMPData" <span v-if="row">{{ row.enable ? "开启" : "关闭" }}</span>
sortable="custom" </template>
ref="weChatMPTable" </el-table-column>
></Table> <el-table-column prop="name" label="模板名称" width="200" sortable />
<Row type="flex" justify="end" class="mt_10"> <el-table-column prop="createTime" label="创建时间" sortable />
<Page <el-table-column label="操作" width="200" align="center" fixed="right">
:current="weChatMPSearchForm.pageNumber" <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" :total="weChatMPTotal"
:page-size="weChatMPSearchForm.pageSize" layout="total, sizes, prev, pager, next"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small" size="small"
></Page> @current-change="changePage"
</Row> @size-change="changePageSize"
</div> />
</TabPane> </div>
</Tabs> </div>
</Card> </el-tab-pane>
</el-tabs>
</el-card>
</div> </div>
</template> </template>
<script> <script>
@@ -101,257 +111,147 @@ import {
getWechatMessagePage, getWechatMessagePage,
editWechatMessageTemplate, editWechatMessageTemplate,
delWechatMessageTemplate, delWechatMessageTemplate,
wechatMPMessageSync, wechatMPMessageSync,
getWechatMPMessagePage, getWechatMPMessagePage,
editWechatMPMessageTemplate, editWechatMPMessageTemplate,
delWechatMPMessageTemplate delWechatMPMessageTemplate,
} from "@/api/setting"; } from "@/api/setting";
export default { export default {
title: "wechat-message-manage", title: "wechat-message-manage",
data() { data() {
return { return {
wechatModal: false,
wechatModal: false,// modal展示 wechatFormData: {},
wechatFormData: {}, // 微信数据 wechatMPFormData: {},
wechatMPFormData: {}, // 微信订阅消息 tab: "WECHAT",
tab: "WECHAT", // tab栏分类 searchForm: { type: "WECHAT" },
searchForm: { // 请求参数 loading: true,
type: "WECHAT" id: "",
}, weChatSearchForm: { pageNumber: 1, pageSize: 20 },
loading: true, // 表单加载状态 weChatMPSearchForm: { pageNumber: 1, pageSize: 20 },
id: '', // 模板id weChatData: [],
//微信消息查询 weChatMPData: [],
weChatSearchForm: { weChatTotal: 0,
// 搜索框对应data对象 weChatMPTotal: 0,
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, // 表单数据总数
}; };
}, },
methods: { methods: {
// 初始化数据
init() { init() {
this.getDataList(); this.getDataList();
}, },
changePage(v) { changePage(v) {
this.searchForm.type = this.tab; 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) { changePageSize(v) {
this.searchForm.type = this.tab; 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) { wechatSettingAlert(v) {
this.wechatFormData = v this.wechatFormData = v;
this.id = v.id this.id = v.id;
this.wechatModal = true this.wechatModal = true;
}, },
//同步微信消息
weChatSync(mp) { weChatSync(mp) {
this.$Modal.confirm({ this.$Modal.confirm({
title: "提示", title: "提示",
// 记得确认修改此处
content: "确认要初始化微信小程序消息订阅?", content: "确认要初始化微信小程序消息订阅?",
loading: true, loading: true,
onOk: () => { onOk: () => {
// 同步微信消息模板
if (mp === "mp") { if (mp === "mp") {
wechatMPMessageSync().then(res => { wechatMPMessageSync().then((res) => {
this.$Modal.remove(); this.$Modal.remove();
if (res.success) { if (res.success) {
this.$Message.success('微信小程序消息订阅初始化'); this.$Message.success("微信小程序消息订阅初始化");
this.getWechatMPMessagePage();
} }
}); });
} else { } else {
// 同步微信消息模板 wechatMessageSync().then((res) => {
wechatMessageSync().then(res => {
this.$Modal.remove(); this.$Modal.remove();
if (res.success) { if (res.success) {
this.$Message.success('微信消息模板初始化成功'); this.$Message.success("微信消息模板初始化成功");
this.getWechatMessagePage();
} }
}); });
} }
},
});
},
wechatFormDataEdit() {
this.$refs.wechatFormData.validate((valid) => {
if (valid) {
if (!this.wechatFormData.updateTime) {
this.wechatFormData.updateTime = "";
}
editWechatMessageTemplate(this.id, this.wechatFormData).then((res) => {
if (res.message === "success") {
this.$Message.success("微信模板修改成功");
this.wechatModal = false;
this.getWechatMessagePage();
}
});
} }
}); });
}, },
//微信设置保存
wechatFormDataEdit() {
this.$refs['wechatFormData'].validate((valid) => {
if (valid) {
if(!this.wechatFormData.updateTime){
this.wechatFormData.updateTime = ''
}
editWechatMessageTemplate(this.id, this.wechatFormData).then(res => {
if (res.message === 'success') {
this.$Message.success('微信模板修改成功');
this.wechatModal = false;
this.getWechatMessagePage();
}
});
}
})
},
wechatMPFormDataEdit() { wechatMPFormDataEdit() {
this.$refs['wechatFormData'].validate((valid) => { this.$refs.wechatFormData.validate((valid) => {
if (valid) { if (valid) {
editWechatMPMessageTemplate(this.id, this.wechatMPFormData).then(res => { editWechatMPMessageTemplate(this.id, this.wechatMPFormData).then((res) => {
if (res.message === 'success') { if (res.message === "success") {
this.$Message.success('微信消息订阅模板修改成功'); this.$Message.success("微信消息订阅模板修改成功");
this.wechatModal = false; this.wechatModal = false;
this.getWechatMessagePage(); this.getWechatMPMessagePage();
} }
}); });
} }
}) });
}, },
//删除微信模消息
delWeChat(v) { delWeChat(v) {
this.$Modal.confirm({ this.$Modal.confirm({
title: "提示", title: "提示",
content: "确定删除此模板?", content: "确定删除此模板?",
loading: true, loading: true,
onOk: () => { onOk: () => {
// 删除微信消息模板
if (this.tab === "WECHAT") { if (this.tab === "WECHAT") {
delWechatMessageTemplate(v.id).then(res => { delWechatMessageTemplate(v.id).then((res) => {
if (res.success) { if (res.success) {
this.$Modal.remove(); this.$Modal.remove();
this.$Message.success('微信模板删除成功'); this.$Message.success("微信模板删除成功");
this.getWechatMessagePage() this.getWechatMessagePage();
} }
}); });
} else { } else {
delWechatMPMessageTemplate(v.id).then(res => { delWechatMPMessageTemplate(v.id).then((res) => {
if (res.success) { if (res.success) {
this.$Modal.remove(); this.$Modal.remove();
this.$Message.success('微信消息订阅删除成功'); this.$Message.success("微信消息订阅删除成功");
this.getWechatMessagePage() this.getWechatMPMessagePage();
} }
}); });
} }
} },
}); });
},
selectDateRange(v) {
if (v) {
this.searchForm.startDate = v[0];
this.searchForm.endDate = v[1];
}
}, },
getDataList() { getDataList() {
this.loading = true; this.getWechatMessagePage();
getWechatMessagePage(this.searchWe).then(res => {
this.loading = false;
if (res.success) {
this.weChatData = res.result.records;
this.weChatTotal = res.result.total;
}
});
}, },
//分页获取微信消息
getWechatMessagePage() { getWechatMessagePage() {
getWechatMessagePage(this.weChatSearchForm).then(res => { this.loading = true;
getWechatMessagePage(this.weChatSearchForm).then((res) => {
this.loading = false; this.loading = false;
if (res.success) { if (res.success) {
this.weChatData = res.result.records; this.weChatData = res.result.records;
@@ -359,9 +259,9 @@ export default {
} }
}); });
}, },
//分页获取微信小程序消息订阅
getWechatMPMessagePage() { getWechatMPMessagePage() {
getWechatMPMessagePage(this.weChatMPSearchForm).then(res => { this.loading = true;
getWechatMPMessagePage(this.weChatMPSearchForm).then((res) => {
this.loading = false; this.loading = false;
if (res.success) { if (res.success) {
this.weChatMPData = res.result.records; this.weChatMPData = res.result.records;
@@ -369,20 +269,30 @@ export default {
} }
}); });
}, },
//tab切换事件 tabPaneChange(tab) {
tabPaneChange(v) { const v = tab.paneName || this.tab;
this.searchForm.type = v; this.searchForm.type = v;
//如果是微信消息则走单独的接口
if (v === "WECHAT") { if (v === "WECHAT") {
this.getWechatMessagePage(); this.getWechatMessagePage();
} else if (v === "WECHATMP") { } else if (v === "WECHATMP") {
this.getWechatMPMessagePage(); this.getWechatMPMessagePage();
} }
},
}
}, },
mounted() { mounted() {
this.init(); this.init();
} },
}; };
</script> </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,518 +1,434 @@
<template> <template>
<div class="search"> <div class="search">
<Card class="points-statistics-card"> <el-card class="points-statistics-card">
<Row type="flex" justify="space-around" align="middle" class="points-statistics"> <el-row type="flex" justify="space-around" align="middle" class="points-statistics">
<Col :xs="24" :sm="12" class="points-statistics-item"> <el-col :xs="24" :sm="12" class="points-statistics-item">
<div class="points-statistics-title">已发放积分数</div> <div class="points-statistics-title">已发放积分数</div>
<div class="points-statistics-subtitle">历史累计发放积分数</div> <div class="points-statistics-subtitle">历史累计发放积分数</div>
<div class="points-statistics-value">{{ formatNumber(pointsStatistics.totalPoint) }}</div> <div class="points-statistics-value">{{ formatNumber(pointsStatistics.totalPoint) }}</div>
</Col> </el-col>
<Col :xs="24" :sm="12" class="points-statistics-item"> <el-col :xs="24" :sm="12" class="points-statistics-item">
<div class="points-statistics-title">未使用积分数</div> <div class="points-statistics-title">未使用积分数</div>
<div class="points-statistics-subtitle">会员账户未使用积分数</div> <div class="points-statistics-subtitle">会员账户未使用积分数</div>
<div class="points-statistics-value">{{ formatNumber(pointsStatistics.unUsedPoint) }}</div> <div class="points-statistics-value">{{ formatNumber(pointsStatistics.unUsedPoint) }}</div>
</Col> </el-col>
</Row> </el-row>
</Card> </el-card>
<div class="point-tabs-wrap"> <div class="point-tabs-wrap">
<Tabs v-model="activeTab" class="point-tabs"> <el-tabs v-model="activeTab" class="point-tabs">
<TabPane label="积分列表" name="pointList"> <el-tab-pane label="积分列表" name="pointList">
<Card class="point-content-card"> <el-card class="point-content-card">
<Form <el-form
@keydown.enter.native="handleMemberSearch" ref="memberSearchForm"
ref="memberSearchForm" :model="memberSearchForm"
:model="memberSearchForm" inline
inline label-width="70px"
:label-width="70" class="search-form"
@submit.native.prevent @keyup.enter="handleMemberSearch"
class="search-form" >
> <el-form-item label="客户名称" prop="nickName">
<Form-item label="客户名称" prop="nickName"> <el-input
<Input v-model="memberSearchForm.nickName"
v-model="memberSearchForm.nickName" placeholder="请输入客户名称"
placeholder="请输入客户名称" clearable
clearable style="width: 180px"
style="width: 180px" />
</el-form-item>
<el-form-item label="客户账号" prop="username">
<el-input
v-model="memberSearchForm.username"
placeholder="请输入客户账号"
clearable
style="width: 180px"
/>
</el-form-item>
<el-form-item label="账号状态" prop="disabled">
<el-select
v-model="memberSearchForm.disabled"
clearable
placeholder="请选择账号状态"
style="width: 160px"
>
<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"
placeholder="最小积分值"
style="width: 140px"
/>
<span class="point-range-separator">-</span>
<el-input-number
v-model="memberSearchForm.maxPoint"
:min="0"
:precision="0"
placeholder="最大积分值"
style="width: 140px"
/>
</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%"
>
<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"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changeMemberPage"
@size-change="changeMemberPageSize"
/> />
</Form-item> </div>
<Form-item label="客户账号" prop="username"> </el-card>
<Input </el-tab-pane>
v-model="memberSearchForm.username" <el-tab-pane label="积分增减记录" name="pointChangeRecord">
placeholder="请输入客户账号" <el-card class="point-content-card">
clearable <el-form
style="width: 180px" ref="searchForm"
:model="searchForm"
inline
label-width="70px"
class="search-form"
@keyup.enter="handleSearch"
>
<el-form-item label="会员名称" prop="username">
<el-input
v-model="searchForm.memberName"
placeholder="请输入会员名称"
clearable
style="width: 240px"
/>
</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
:data="data"
ref="table"
class="mt_10 point-table"
style="width: 100%"
>
<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"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/> />
</Form-item> </div>
<Form-item label="账号状态" prop="disabled"> </el-card>
<Select </el-tab-pane>
v-model="memberSearchForm.disabled" </el-tabs>
clearable
placeholder="请选择账号状态"
style="width: 160px"
>
<Option value="OPEN">启用</Option>
<Option value="CLOSE">禁用</Option>
</Select>
</Form-item>
<Form-item label="积分值">
<InputNumber
v-model="memberSearchForm.minPoint"
:min="0"
:precision="0"
placeholder="最小积分值"
style="width: 140px"
/>
<span class="point-range-separator">-</span>
<InputNumber
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"
:data="memberData"
class="member-table"
>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="memberSearchForm.pageNumber"
:total="memberTotal"
:page-size="memberSearchForm.pageSize"
@on-change="changeMemberPage"
@on-page-size-change="changeMemberPageSize"
:page-size-opts="[20, 50, 100]"
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"
ref="searchForm"
:model="searchForm"
inline
:label-width="70"
@submit.native.prevent
class="search-form"
>
<Form-item label="会员名称" prop="username">
<Input
type="text"
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"
border
:columns="columns"
:data="data"
ref="table"
class="mt_10 point-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>
</TabPane>
</Tabs>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import * as API_Member from "@/api/member.js";
import * as API_Member from "@/api/member.js"; export default {
name: "point",
export default { data() {
// 积分历史页面 return {
name: "point", activeTab: "pointList",
data() { loading: true,
return { memberLoading: false,
activeTab: "pointList", pointsStatistics: {
loading: true, // 表单加载状态 totalPoint: 0,
memberLoading: false, unUsedPoint: 0,
pointsStatistics: { },
totalPoint: 0, searchForm: {
unUsedPoint: 0, pageNumber: 1,
}, pageSize: 20,
searchForm: { // 请求参数 },
pageNumber: 1, memberSearchForm: {
pageSize: 20, pageNumber: 1,
}, pageSize: 20,
memberSearchForm: { nickName: "",
pageNumber: 1, username: "",
pageSize: 20, disabled: "",
nickName: "", minPoint: null,
username: "", maxPoint: null,
disabled: "", },
minPoint: null, data: [],
maxPoint: null, memberData: [],
}, memberTotal: 0,
columns: [ total: 0,
{ };
title: "会员名称", },
key: "memberName", methods: {
minWidth: 120, callback(val) {
tooltip: true this.$emit("callback", val);
},
{
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: [], // 表单数据
memberData: [],
memberTotal: 0,
total: 0, // 表单数据总数
};
}, },
methods: { detail(row) {
// 回调给父级 this.$filters.customRouterPush({ name: "member-detail", query: { id: row.id } });
callback(val) { },
this.$emit("callback", val); init() {
}, this.getStatistics();
// 查看会员详情 this.getMemberList();
detail(row) { this.getData();
this.$options.filters.customRouterPush({ name: "member-detail", query: { id: row.id } }); },
}, getStatistics() {
// 初始化数据 API_Member.queryMemberPointsStatistics().then((res) => {
init() { if (res && res.success && res.result) {
this.getStatistics(); this.pointsStatistics = {
this.getMemberList(); totalPoint: res.result.totalPoint || 0,
this.getData(); unUsedPoint: res.result.unUsedPoint || 0,
}, };
getStatistics() { }
API_Member.queryMemberPointsStatistics().then((res) => { });
if (res && res.success && res.result) { },
this.pointsStatistics = { formatNumber(value) {
totalPoint: res.result.totalPoint || 0, const numericValue = Number(value || 0);
unUsedPoint: res.result.unUsedPoint || 0, if (!Number.isFinite(numericValue)) return "0";
}; return numericValue.toLocaleString();
} },
}); changePage(v) {
}, this.searchForm.pageNumber = v;
formatNumber(value) { this.getData();
const numericValue = Number(value || 0); },
if (!Number.isFinite(numericValue)) return "0"; changePageSize(v) {
return numericValue.toLocaleString(); this.searchForm.pageNumber = 1;
}, this.searchForm.pageSize = v;
// 分页 改变页码 this.getData();
changePage(v) { },
this.searchForm.pageNumber = v; handleSearch() {
this.getData(); this.searchForm.pageNumber = 1;
}, this.searchForm.pageSize = 20;
// 分页 改变页数 this.getData();
changePageSize(v) { },
this.searchForm.pageNumber = 1; handleMemberSearch() {
this.searchForm.pageSize = v; this.memberSearchForm.pageNumber = 1;
this.getData(); this.getMemberList();
}, },
// 搜索 changeMemberPage(v) {
handleSearch() { this.memberSearchForm.pageNumber = v;
this.searchForm.pageNumber = 1; this.getMemberList();
this.searchForm.pageSize = 20; },
this.getData(); changeMemberPageSize(v) {
}, this.memberSearchForm.pageNumber = 1;
handleMemberSearch() { this.memberSearchForm.pageSize = v;
this.memberSearchForm.pageNumber = 1; this.getMemberList();
this.getMemberList(); },
}, getMemberList() {
changeMemberPage(v) { this.memberLoading = true;
this.memberSearchForm.pageNumber = v; const params = {
this.getMemberList(); pageNumber: this.memberSearchForm.pageNumber,
}, pageSize: this.memberSearchForm.pageSize,
changeMemberPageSize(v) { nickName: this.memberSearchForm.nickName || undefined,
this.memberSearchForm.pageNumber = 1; username: this.memberSearchForm.username || undefined,
this.memberSearchForm.pageSize = v; disabled: this.memberSearchForm.disabled || undefined,
this.getMemberList(); minPoint: this.memberSearchForm.minPoint,
}, maxPoint: this.memberSearchForm.maxPoint,
getMemberList() { };
this.memberLoading = true; API_Member.getMemberListData(params)
const params = { .then((res) => {
pageNumber: this.memberSearchForm.pageNumber,
pageSize: this.memberSearchForm.pageSize,
nickName: this.memberSearchForm.nickName || undefined,
username: this.memberSearchForm.username || undefined,
disabled: this.memberSearchForm.disabled || undefined,
minPoint: this.memberSearchForm.minPoint,
maxPoint: this.memberSearchForm.maxPoint,
};
API_Member.getMemberListData(params).then((res) => {
this.memberLoading = false; this.memberLoading = false;
if (res && res.success && res.result && res.result.records) { if (res && res.success && res.result && res.result.records) {
this.memberData = res.result.records; this.memberData = res.result.records;
this.memberTotal = res.result.total; this.memberTotal = res.result.total;
} }
}).catch(() => { })
.catch(() => {
this.memberLoading = false; this.memberLoading = false;
}); });
}, },
//查新积分列表 getData() {
getData() { this.loading = true;
this.loading = true; API_Member.getHistoryPointData(this.searchForm).then((res) => {
API_Member.getHistoryPointData(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
});
this.loading = false; this.loading = false;
}, if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
});
}, },
mounted() { },
this.init(); mounted() {
}, this.init();
}; },
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.points-statistics-card { .points-statistics-card {
margin-bottom: 10px; margin-bottom: 10px;
}
.point-tabs-wrap {
padding: 12px 16px 16px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(23, 35, 61, 0.04);
}
.point-tabs {
:deep(.el-tabs__header) {
margin-bottom: 16px;
} }
.point-tabs-wrap { :deep(.el-tabs__item) {
padding: 12px 16px 16px; padding: 8px 18px;
background: #fff; color: #515a6e;
border-radius: 8px; transition: all 0.2s ease;
box-shadow: 0 2px 10px rgba(23, 35, 61, 0.04);
} }
.point-tabs { :deep(.el-tabs__item.is-active) {
::v-deep .ivu-tabs-bar { color: #409eff;
margin-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
}
::v-deep .ivu-tabs-nav .ivu-tabs-tab {
padding: 8px 18px;
color: #515a6e;
background: #fff;
transition: all 0.2s ease;
}
::v-deep .ivu-tabs-nav .ivu-tabs-tab-active {
color: #2d8cf0;
font-weight: 500;
}
::v-deep .ivu-tabs-ink-bar {
height: 2px;
border-radius: 2px;
}
}
.points-statistics {
width: 100%;
}
.points-statistics-item {
padding: 10px 0;
text-align: center;
}
.points-statistics-title {
font-size: 14px;
color: #17233d;
line-height: 20px;
}
.points-statistics-subtitle {
font-size: 12px;
color: #808695;
line-height: 18px;
margin-top: 4px;
}
.points-statistics-value {
font-size: 18px;
font-weight: 600;
color: #fa6419;
line-height: 26px;
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 {
border-bottom: 1px solid #f5f5f5;
}
}
.card-title {
font-size: 14px;
font-weight: 500; font-weight: 500;
color: #17233d;
} }
.member-table { :deep(.el-tabs__active-bar) {
::v-deep .ivu-table-header th { height: 2px;
background: #fafafa; border-radius: 2px;
}
::v-deep .ivu-tag {
margin-right: 0;
}
} }
}
.point-range-separator { .points-statistics {
display: inline-block; width: 100%;
margin: 0 8px; }
color: #808695;
}
.face { .points-statistics-item {
width: 60px; padding: 10px 0;
height: 60px; text-align: center;
border-radius: 50%; }
.points-statistics-title {
font-size: 14px;
color: #17233d;
line-height: 20px;
}
.points-statistics-subtitle {
font-size: 12px;
color: #808695;
line-height: 18px;
margin-top: 4px;
}
.points-statistics-value {
font-size: 18px;
font-weight: 600;
color: #fa6419;
line-height: 26px;
margin-top: 8px;
}
.point-content-card {
margin-bottom: 12px;
}
.member-list-card {
:deep(.el-card__header) {
border-bottom: 1px solid #f5f5f5;
} }
}
.card-title {
font-size: 14px;
font-weight: 500;
color: #17233d;
}
.member-table {
:deep(.el-table__header th) {
background: #fafafa;
}
}
.point-range-separator {
display: inline-block;
margin: 0 8px;
color: #808695;
}
.link-text {
color: #409eff;
cursor: pointer;
text-decoration: none;
}
.face {
width: 60px;
height: 60px;
border-radius: 50%;
}
</style> </style>

View File

@@ -1,351 +1,197 @@
<template> <template>
<div class="search"> <div class="search">
<Card> <el-card>
<Tabs value="RETURN_MONEY" @on-click="handleClickType"> <el-tabs v-model="activeTab" @tab-click="onTabClick">
<TabPane label="退款" name="RETURN_MONEY"> <el-tab-pane label="退款" name="RETURN_MONEY">
<Row class="operation" style="margin-bottom: 10px"> <div class="operation" style="margin-bottom: 10px">
<Button @click="add" type="primary" >添加</Button> <el-button type="primary" @click="add">添加</el-button>
</Row> </div>
<Table </el-tab-pane>
:loading="loading" <el-tab-pane label="取消" name="CANCEL">
border <div class="operation" style="margin-bottom: 10px">
:columns="columns" <el-button type="primary" @click="add">添加</el-button>
:data="data" </div>
ref="table" </el-tab-pane>
></Table> <el-tab-pane label="退货" name="RETURN_GOODS">
<Row type="flex" justify="end" class="mt_10"> <div class="operation" style="margin-bottom: 10px">
<Page <el-button type="primary" @click="add">添加</el-button>
:current="searchForm.pageNumber" </div>
:total="total" </el-tab-pane>
:page-size="searchForm.pageSize" <el-tab-pane label="投诉" name="COMPLAIN">
@on-change="changePage" <div class="operation" style="margin-bottom: 10px">
@on-page-size-change="changePageSize" <el-button type="primary" @click="add">添加</el-button>
:page-size-opts="[20, 50, 100]" <el-button @click="getDataList">刷新</el-button>
size="small" </div>
show-total </el-tab-pane>
show-elevator </el-tabs>
show-sizer
></Page> <el-table v-loading="loading" border :data="data" ref="table" style="width: 100%">
</Row> <el-table-column prop="createBy" label="创建人" min-width="120" />
</TabPane> <el-table-column prop="reason" label="原因" min-width="400" />
<TabPane label="取消" name="CANCEL"> <el-table-column prop="createTime" label="时间" min-width="100" />
<Row class="operation" style="margin-bottom: 10px"> <el-table-column label="操作" width="200" align="center">
<Button @click="add" type="primary" icon="md-add">添加</Button> <template #default="{ row }">
</Row> <template v-if="row">
<Table <a class="link-text" @click="edit(row)">编辑</a>
:loading="loading" <span class="op-split">|</span>
border <a class="link-text" @click="remove(row)">删除</a>
:columns="columns" </template>
:data="data" </template>
ref="table" </el-table-column>
></Table> </el-table>
<Row type="flex" justify="end" class="mt_10">
<Page <div class="mt_10" style="display: flex; justify-content: flex-end">
:current="searchForm.pageNumber" <el-pagination
:total="total" v-model:current-page="searchForm.pageNumber"
:page-size="searchForm.pageSize" v-model:page-size="searchForm.pageSize"
@on-change="changePage" :page-sizes="[20, 50, 100]"
@on-page-size-change="changePageSize" :total="total"
:page-size-opts="[20, 50, 100]" layout="total, sizes, prev, pager, next, jumper"
size="small" size="small"
show-total @current-change="changePage"
show-elevator @size-change="changePageSize"
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
>
</div> </div>
</Modal> </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> </div>
</template> </template>
<script> <script>
import * as API_Order from "@/api/order"; import * as API_Order from "@/api/order";
export default { export default {
data() { data() {
return { return {
modalVisible: false,//添加售后原因弹出框 activeTab: "RETURN_MONEY",
modalTitle: "", //添加售后原因弹出框标题 modalVisible: false,
loading: true, // 表单加载状态 modalTitle: "",
submitLoading: false, // 添加或编辑提交状态 loading: true,
form: { submitLoading: false,
reason: "" form: { reason: "" },
},//添加编辑表单 formValidate: {
formValidate: { reason: [{ required: true, message: "请输入售后原因", trigger: "blur" }],
reason: [ },
{ searchForm: {
required: true, pageNumber: 1,
message: "请输入售后原因", pageSize: 20,
trigger: "blur", sort: "createTime",
}, order: "desc",
], serviceType: "RETURN_MONEY",
}, },
searchForm: { data: [],
// 搜索框初始化对象 total: 0,
pageNumber: 1, // 当前页数 };
pageSize: 20, // 页面大小 },
sort: "createTime", // 默认排序字段 methods: {
order: "desc", // 默认排序方式 onTabClick(tab) {
serviceType: "RETURN_MONEY" this.handleClickType(tab.paneName);
},
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,//条数
};
}, },
changePage(v) {
methods: { this.searchForm.pageNumber = v;
// 分页 修改页码
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.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.loading = false;
},
//添加售后原因
add() {
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 = "修改售后原因"
},
//提交表单
handleSubmit() {
this.form.serviceType = this.searchForm.serviceType
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
if (this.modalTitle == '添加售后原因') {
// 添加
delete this.form.id;
API_Order.addAfterSaleReason(this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("添加成功");
this.getDataList();
this.modalVisible = false;
}
});
} else {
// 编辑
API_Order.editAfterSaleReason(this.form.id, this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("修改成功");
this.getDataList();
this.modalVisible = false;
}
});
}
}
});
},
//删除售后原因
remove(v) {
this.$Modal.confirm({
title: "确认删除",
// 记得确认修改此处
content: "确认要删除此售后原因?",
loading: true,
onOk: () => {
// 删除
API_Order.delAfterSaleReason(v.id).then((res) => {
this.$Modal.remove();
if (res.success) {
this.$Message.success("售后原因已删除");
this.getDataList();
}
});
},
});
}
},
mounted() {
this.getDataList(); this.getDataList();
}, },
}; changePageSize(v) {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getDataList();
},
handleClickType(v) {
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;
}
});
},
add() {
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 = "修改售后原因";
},
handleSubmit() {
this.form.serviceType = this.searchForm.serviceType;
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
if (this.modalTitle == "添加售后原因") {
delete this.form.id;
API_Order.addAfterSaleReason(this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("添加成功");
this.getDataList();
this.modalVisible = false;
}
});
} else {
API_Order.editAfterSaleReason(this.form.id, this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("修改成功");
this.getDataList();
this.modalVisible = false;
}
});
}
}
});
},
remove(v) {
this.$Modal.confirm({
title: "确认删除",
content: "确认要删除此售后原因?",
loading: true,
onOk: () => {
API_Order.delAfterSaleReason(v.id).then((res) => {
this.$Modal.remove();
if (res.success) {
this.$Message.success("售后原因已删除");
this.getDataList();
}
});
},
});
},
},
mounted() {
this.getDataList();
},
};
</script> </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