update 更新商品类型,绑定规格和绑定分类

This commit is contained in:
qijq 2025-07-19 01:28:45 +08:00
parent f803e7d4d6
commit 686ac82724
7 changed files with 1027 additions and 883 deletions

View File

@ -1,7 +1,7 @@
import http from "../../utils/http"; import http from "../../utils/http";
import config from "../../config/config"; import config from "../../config/config";
/** /**
* *
* @author Seven * @author Seven
* @data 2025-7-12 * @data 2025-7-12
@ -21,3 +21,73 @@ export function GetTypeManageList(params) {
baseURL: config.adminApi, baseURL: config.adminApi,
}); });
} }
/**
*
* @author Seven
* @data 2025-7-17
* @params {
* store_id
type_name
type_is_param
type_brand_ids
type_buildin
type_is_assist
type_spec_ids: "1112,1113" 规格ID
type_id
type_category_id 分类ID
type_is_draft
type_is_entity
type_is_brand
store_name
type_remark
type_assist_ids
* }
* @returns { }
* @see https://mall.gpxscs.cn/api/admin/shop/shop-base-product-type/edit?source_lang=zh_CN
*/
export function UpdateType(params) {
return http({
url: "/shop/shop-base-product-type/edit",
method: "post",
params,
baseURL: config.adminApi,
});
}
/**
*
* @author Seven
* @data 2025-7-17
* @params {
* type_ids
* }
* @returns { }
* @see https://mall.gpxscs.cn/api/admin/shop/shop-base-product-type/delete
*/
export function DelectType(params) {
return http({
url: "shop/shop-base-product-type/delete",
method: "post",
params,
baseURL: config.adminApi,
});
}
/**
*
* @author Seven
* @data 2025-7-17
* @returns { }
* @see https://mall.gpxscs.cn/api/admin/shop/shop-base-product-spec/specMap
*/
export function GetAllSpecification() {
return http({
url: "/shop/shop-base-product-spec/specMap",
method: "get",
baseURL: config.adminApi,
});
}

View File

@ -1,124 +0,0 @@
## 树形层级选择器
### 简介
为统一样式而生树形层级选择器picker弹窗形式的样式和比例参照uniapp的picker和uni-data-picker组件
* 支持单选、多选、父级选择,当然也支持单层选择
* 支持Object对象属性自定义映射
* 支持显示全部选中、部分选中、未选中三种状态
* 支持快速自定义简单样式分割线、按钮、标题、对齐等深入样式可复写css
### 使用方法
`script` 中引入组件
``` javascript
import baTreePicker from "@/components/ba-tree-picker/ba-tree-picker.vue"
export default {
components: {
baTreePicker
}
```
`template` 中使用组件
``` javascript
<ba-tree-picker ref="treePicker" :multiple='false' @select-change="selectChange" title="选择城市"
:localdata="listData" valueKey="value" textKey="label" childrenKey="children" />
```
`script` 中定义打开方法,和选择监听
``` javascript
methods: {
// 显示选择器
showPicker() {
this.$refs.treePicker._show();
},
//监听选择ids为数组
selectChange(ids, names) {
console.log(ids, names)
}
}
```
`template` 中调用打开
``` javascript
<view @click="showPicker">调用选择器</view>
```
### 属性
|属性名|类型|默认值|说明|
|:-|:-:|:--:|-:|
|localdata|Array|[]|源数据目前支持tree结构后续会考虑支持扁平化结构|
|valueKey|String|id|指定 Object 中 key 的值作为节点数据id|
|textKey|String|name|指定 Object 中 key 的值作为节点显示内容|
|childrenKey|String|children|指定 Object 中 key 的值作为节点子集|
|multiple|Boolean|false|是否多选,默认单选|
|selectParent|Boolean|true|是否可以选父级,默认可以|
|title|String| |标题|
|titleColor|String||标题颜色|
|confirmColor|String|#0055ff|确定按钮颜色|
|cancelColor|String|#757575|取消按钮颜色|
|switchColor|String|#666|节点切换图标颜色|
|border|Boolean|false|是否有分割线,默认无|
### 数据格式
注意必须有id、name(id可通过valueKey来配置为其它键值如value)字段,且唯一
``` json
[
{
id: 1,
name: '公司1',
children: [{
id: 11,
name: '研发部',
children: [{
id: 111,
name: '张三',
},{
id: 112,
name: '李四',
}]
},{
id: 12,
name: '综合部',
} ]
},
{
id: 2,
name: '公司2',
children: [{
id: 21,
name: '研发部',
},{
id: 22,
name: '综合部',
},{
id: 23,
name: '财务部',
}, ]
},
{
id: 3,
name: '公司3'
},
{
id: 4,
name: '公司4',
children: [{
id: 41,
name: '研发部',
}]
}
]
```
</details>
### 方法
|方法名|参数|默认值|说明|
|:-|:-:|:--:|-:|
|_show()| | |显示选择器|
|_hide()| | |隐藏选择器|

View File

@ -1,704 +0,0 @@
<!-- 树形层级选择器-->
<!-- 1支持单选多选 -->
<template>
<view class="ba-tree-picker">
<view
class="tree-cover"
:class="{ show: showDialog }"
@tap="_cancel"
></view>
<view class="tree-dialog" :class="{ show: showDialog }">
<view class="tree-bar">
<view
class="tree-bar-cancel"
:style="{ color: cancelColor }"
hover-class="hover-c"
@tap="_cancel"
>
取消
</view>
<view class="tree-bar-title" :style="{ color: titleColor }">
{{ title }}
</view>
<view
class="tree-bar-confirm"
:style="{ color: confirmColor }"
hover-class="hover-c"
@tap="_confirm"
>
{{ multiple ? "确定" : "" }}
</view>
</view>
<view class="tree-view">
<scroll-view class="tree-list" :scroll-y="true">
<block v-for="(item, index) in treeList" :key="index">
<view
class="tree-item"
:style="[
{
paddingLeft: item.level * 30 + 'rpx',
},
]"
:class="{
itemBorder: border === true,
show: item.isShow,
}"
>
<view class="item-label">
<view
class="item-icon uni-inline-item"
@tap.stop="_onItemSwitch(item, index)"
>
<view
v-if="!item.isLastLevel && item.isShowChild"
class="switch-on"
:style="{ 'border-left-color': switchColor }"
></view>
<view
v-else-if="!item.isLastLevel && !item.isShowChild"
class="switch-off"
:style="{ 'border-top-color': switchColor }"
></view>
<view
v-else
class="item-last-dot"
:style="{ 'border-top-color': switchColor }"
></view>
</view>
<view
class="uni-flex-item uni-inline-item"
@tap.stop="_onItemSelect(item, index)"
>
<view class="item-name">
{{
item.name +
(item.childCount ? "(" + item.childCount + ")" : "")
}}
</view>
<view
class="item-check"
v-if="selectParent ? true : item.isLastLevel"
>
<view
class="item-check-yes"
v-if="item.checkStatus == 1"
:class="{ radio: !multiple }"
:style="{ 'border-color': confirmColor }"
>
<view
class="item-check-yes-part"
:style="{ 'background-color': confirmColor }"
></view>
</view>
<view
class="item-check-yes"
v-else-if="item.checkStatus == 2"
:class="{ radio: !multiple }"
:style="{ 'border-color': confirmColor }"
>
<view
class="item-check-yes-all"
:style="{ 'background-color': confirmColor }"
></view>
</view>
<view
class="item-check-no"
v-else
:class="{ radio: !multiple }"
:style="{ 'border-color': confirmColor }"
></view>
</view>
</view>
</view>
</view>
</block>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
export default {
emits: ["select-change"],
name: "ba-tree-picker",
props: {
valueKey: {
type: String,
default: "id",
},
textKey: {
type: String,
default: "name",
},
childrenKey: {
type: String,
default: "children",
},
localdata: {
type: Array,
default: function () {
return [];
},
},
localTreeList: {
//
type: Array,
default: function () {
return [];
},
},
selectedData: {
type: Array,
default: function () {
return [];
},
},
title: {
type: String,
default: "",
},
multiple: {
//
type: Boolean,
default: true,
},
selectParent: {
//
type: Boolean,
default: true,
},
confirmColor: {
//
type: String,
default: "", // #0055ff
},
cancelColor: {
//
type: String,
default: "", // #757575
},
titleColor: {
//
type: String,
default: "", //
},
switchColor: {
//
type: String,
default: "", // #666
},
border: {
// 线
type: Boolean,
default: false,
},
},
data() {
return {
showDialog: false,
treeList: [],
};
},
computed: {},
methods: {
_show() {
this.showDialog = true;
},
_hide() {
this.showDialog = false;
},
_cancel() {
this._hide();
this.$emit("cancel", "");
},
_confirm() {
//
let selectedList = []; // id
let selectedNames;
let currentLevel = -1;
this.treeList.forEach((item, index) => {
if (currentLevel >= 0 && item.level > currentLevel) {
} else {
if (item.checkStatus === 2) {
currentLevel = item.level;
selectedList.push(item.id);
selectedNames = selectedNames
? selectedNames + " / " + item.name
: item.name;
} else {
currentLevel = -1;
}
}
});
//console.log('_confirm', selectedList);
this._hide();
this.$emit("select-change", selectedList, selectedNames);
},
//tree
_formatTreeData(list = [], level = 0, parentItem, isShowChild = true) {
let nextIndex = 0;
let parentId = -1;
let initCheckStatus = 0;
if (parentItem) {
nextIndex =
this.treeList.findIndex((item) => item.id === parentItem.id) + 1;
parentId = parentItem.id;
if (!this.multiple) {
//
initCheckStatus = 0;
} else initCheckStatus = parentItem.checkStatus == 2 ? 2 : 0;
}
list.forEach((item) => {
let isLastLevel = true;
if (item && item[this.childrenKey]) {
let children = item[this.childrenKey];
if (Array.isArray(children) && children.length > 0) {
isLastLevel = false;
}
}
let itemT = {
id: item[this.valueKey],
name: item[this.textKey],
level,
isLastLevel,
isShow: isShowChild,
isShowChild: false,
checkStatus: initCheckStatus,
orCheckStatus: 0,
parentId,
children: item[this.childrenKey],
childCount: item[this.childrenKey]
? item[this.childrenKey].length
: 0,
childCheckCount: 0,
childCheckPCount: 0,
};
if (this.selectedData.indexOf(itemT.id) >= 0) {
itemT.checkStatus = 2;
itemT.orCheckStatus = 2;
itemT.childCheckCount = itemT.children ? itemT.children.length : 0;
this._onItemParentSelect(itemT, nextIndex);
}
this.treeList.splice(nextIndex, 0, itemT);
nextIndex++;
});
//console.log(this.treeList);
},
//
_onItemSwitch(item, index) {
// console.log(item)
//console.log('_itemSwitch')
if (item.isLastLevel === true) {
return;
}
item.isShowChild = !item.isShowChild;
if (item.children) {
this._formatTreeData(item.children, item.level + 1, item);
item.children = undefined;
} else {
this._onItemChildSwitch(item, index);
}
},
_onItemChildSwitch(item, index) {
//console.log('_onItemChildSwitch')
const firstChildIndex = index + 1;
if (firstChildIndex > 0)
for (var i = firstChildIndex; i < this.treeList.length; i++) {
let itemChild = this.treeList[i];
if (itemChild.level > item.level) {
if (item.isShowChild) {
if (itemChild.parentId === item.id) {
itemChild.isShow = item.isShowChild;
if (!itemChild.isShow) {
itemChild.isShowChild = false;
}
}
} else {
itemChild.isShow = item.isShowChild;
itemChild.isShowChild = false;
}
} else {
return;
}
}
},
//
_onItemSelect(item, index) {
//console.log('_onItemSelect')
//console.log(item)
if (!this.multiple) {
//
item.checkStatus = item.checkStatus == 0 ? 2 : 0;
this.treeList.forEach((v, i) => {
if (i != index) {
this.treeList[i].checkStatus = 0;
} else {
this.treeList[i].checkStatus = 2;
}
});
let selectedList = [];
let selectedNames;
selectedList.push(item.id);
selectedNames = item.name;
this._hide();
this.$emit("select-change", selectedList, selectedNames);
return;
}
let oldCheckStatus = item.checkStatus;
switch (oldCheckStatus) {
case 0:
item.checkStatus = 2;
item.childCheckCount = item.childCount;
item.childCheckPCount = 0;
break;
case 1:
case 2:
item.checkStatus = 0;
item.childCheckCount = 0;
item.childCheckPCount = 0;
break;
default:
break;
}
//
this._onItemChildSelect(item, index);
//
this._onItemParentSelect(item, index, oldCheckStatus);
},
_onItemChildSelect(item, index) {
//console.log('_onItemChildSelect')
let allChildCount = 0;
if (item.childCount && item.childCount > 0) {
index++;
while (
index < this.treeList.length &&
this.treeList[index].level > item.level
) {
let itemChild = this.treeList[index];
itemChild.checkStatus = item.checkStatus;
if (itemChild.checkStatus == 2) {
itemChild.childCheckCount = itemChild.childCount;
itemChild.childCheckPCount = 0;
} else if (itemChild.checkStatus == 0) {
itemChild.childCheckCount = 0;
itemChild.childCheckPCount = 0;
}
// console.log('>>>>index', index, 'item', itemChild.name, ' status', itemChild
// .checkStatus)
index++;
}
}
},
_onItemParentSelect(item, index, oldCheckStatus) {
//console.log('_onItemParentSelect')
//console.log(item)
const parentIndex = this.treeList.findIndex(
(itemP) => itemP.id == item.parentId
);
//console.log('parentIndex' + parentIndex)
if (parentIndex >= 0) {
let itemParent = this.treeList[parentIndex];
let count = itemParent.childCheckCount;
let oldCheckStatusParent = itemParent.checkStatus;
if (oldCheckStatus == 1) {
itemParent.childCheckPCount -= 1;
} else if (oldCheckStatus == 2) {
itemParent.childCheckCount -= 1;
}
if (item.checkStatus == 1) {
itemParent.childCheckPCount += 1;
} else if (item.checkStatus == 2) {
itemParent.childCheckCount += 1;
}
if (
itemParent.childCheckCount <= 0 &&
itemParent.childCheckPCount <= 0
) {
itemParent.childCheckCount = 0;
itemParent.childCheckPCount = 0;
itemParent.checkStatus = 0;
} else if (itemParent.childCheckCount >= itemParent.childCount) {
itemParent.childCheckCount = itemParent.childCount;
itemParent.childCheckPCount = 0;
itemParent.checkStatus = 2;
} else {
itemParent.checkStatus = 1;
}
//console.log('itemParent', itemParent)
this._onItemParentSelect(itemParent, parentIndex, oldCheckStatusParent);
}
},
//
_reTreeList() {
this.treeList.forEach((v, i) => {
this.treeList[i].checkStatus = v.orCheckStatus;
});
},
_initTree() {
this.treeList = [];
this._formatTreeData(this.localdata);
},
},
watch: {
localdata() {
this._initTree();
},
localTreeList() {
this.treeList = this.localTreeList;
},
},
mounted() {
this._initTree();
},
};
</script>
<style scoped>
.ba-tree-picker {
z-index: 12000;
}
.tree-cover {
position: fixed;
top: 0rpx;
right: 0rpx;
bottom: 0rpx;
left: 0rpx;
z-index: 100;
background-color: rgba(0, 0, 0, 0.4);
opacity: 0;
transition: all 0.3s ease;
visibility: hidden;
z-index: 12000;
}
.tree-cover.show {
visibility: visible;
opacity: 1;
}
.tree-dialog {
position: fixed;
top: 0rpx;
right: 0rpx;
bottom: 0rpx;
left: 0rpx;
background-color: #fff;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
z-index: 12000;
top: 20%;
transition: all 0.3s ease;
transform: translateY(100%);
}
.tree-dialog.show {
transform: translateY(0);
}
.tree-bar {
/* background-color: #fff; */
height: 90rpx;
padding-left: 25rpx;
padding-right: 25rpx;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
border-bottom-width: 1rpx !important;
border-bottom-style: solid;
border-bottom-color: #f5f5f5;
font-size: 32rpx;
color: #757575;
line-height: 1;
}
.tree-bar-confirm {
color: #0055ff;
padding: 15rpx;
}
.tree-bar-title {
}
.tree-bar-cancel {
color: #757575;
padding: 15rpx;
}
.tree-view {
flex: 1;
padding: 20rpx;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
overflow: hidden;
height: 100%;
}
.tree-list {
flex: 1;
height: 100%;
overflow: hidden;
}
.tree-item {
display: flex;
justify-content: space-between;
align-items: center;
line-height: 1;
height: 0;
opacity: 0;
transition: 0.2s;
overflow: hidden;
}
.tree-item.show {
height: 90rpx;
opacity: 1;
}
.tree-item.showchild:before {
transform: rotate(90deg);
}
.tree-item.last:before {
opacity: 0;
}
.switch-on {
width: 0;
height: 0;
border-left: 10rpx solid transparent;
border-right: 10rpx solid transparent;
border-top: 15rpx solid #666;
}
.switch-off {
width: 0;
height: 0;
border-bottom: 10rpx solid transparent;
border-top: 10rpx solid transparent;
border-left: 15rpx solid #666;
}
.item-last-dot {
position: absolute;
width: 10rpx;
height: 10rpx;
border-radius: 100%;
background: #666;
}
.item-icon {
width: 26rpx;
height: 26rpx;
margin-right: 8rpx;
padding-right: 20rpx;
padding-left: 20rpx;
}
.item-label {
flex: 1;
display: flex;
align-items: center;
height: 100%;
line-height: 1.2;
}
.item-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 450rpx;
}
.item-check {
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
}
.item-check-yes,
.item-check-no {
width: 20px;
height: 20px;
border-top-left-radius: 20%;
border-top-right-radius: 20%;
border-bottom-right-radius: 20%;
border-bottom-left-radius: 20%;
border-top-width: 1rpx;
border-left-width: 1rpx;
border-bottom-width: 1rpx;
border-right-width: 1rpx;
border-style: solid;
border-color: #0055ff;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
}
.item-check-yes-part {
width: 12px;
height: 12px;
border-top-left-radius: 20%;
border-top-right-radius: 20%;
border-bottom-right-radius: 20%;
border-bottom-left-radius: 20%;
background-color: #0055ff;
}
.item-check-yes-all {
margin-bottom: 5px;
border: 2px solid #007aff;
border-left: 0;
border-top: 0;
height: 12px;
width: 6px;
transform-origin: center;
/* #ifndef APP-NVUE */
transition: all 0.3s;
/* #endif */
transform: rotate(45deg);
}
.item-check .radio {
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
}
.item-check .radio .item-check-yes-b {
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
}
.hover-c {
opacity: 0.6;
}
.itemBorder {
border-bottom: 1px solid #e5e5e5;
}
</style>

View File

@ -286,7 +286,19 @@
> >
<view class="commodity-type-popup-content"> <view class="commodity-type-popup-content">
<view class="commodity-type-popup-title">选择商品类型</view> <view class="commodity-type-popup-title">选择商品类型</view>
<view class="no-commodity-type" v-if="typeManageList.length <= 0">
<view class="no-commodity-type-bg"></view>
<view class="no-commodity-type-tips" @click="skipuBrandList">
暂无商品类型点击马上添加
<u-icon
style="display: inline-block"
name="arrow-right"
color="red"
></u-icon>
</view>
</view>
<scroll-view <scroll-view
v-if="typeManageList.length > 0"
scroll-y scroll-y
class="commodity-type-search-scroll" class="commodity-type-search-scroll"
:show-scrollbar="false" :show-scrollbar="false"
@ -915,6 +927,23 @@ export default {
} }
} }
.no-commodity-type {
.no-commodity-type-bg {
margin: 6% auto;
margin-bottom: 0;
width: 300rpx;
height: 200rpx;
background-image: url("../../../static/warehouse/no-commodity-type.png");
background-size: 100%;
}
.no-commodity-type-tips {
margin: 80rpx auto;
color: red;
text-align: center;
}
}
.affirm-popup { .affirm-popup {
::v-deep.u-popup__content { ::v-deep.u-popup__content {
border-radius: 16rpx; border-radius: 16rpx;

View File

@ -31,11 +31,10 @@
<view class="specification-name">{{ item.spec_name }}</view> <view class="specification-name">{{ item.spec_name }}</view>
<view class="specification-classify"> <view class="specification-classify">
分类 分类
{{ <text style="color: red" v-if="item.spec_category_id == 0">
item.spec_category_id == 0 未绑定分类
? "未绑定分类" </text>
: handerCategoryName(item) <text v-else>{{ handerCategoryName(item) }}</text>
}}
</view> </view>
<view class="specification-sort">排序{{ item.spec_order }}</view> <view class="specification-sort">排序{{ item.spec_order }}</view>
</view> </view>
@ -91,6 +90,7 @@
zIndex="10077" zIndex="10077"
:show="showAddAndEditPopup" :show="showAddAndEditPopup"
mode="center" mode="center"
@close="handerCloseAddAndEditPopup"
> >
<view class="add-popup-content"> <view class="add-popup-content">
<view class="add-popup-title"> <view class="add-popup-title">
@ -174,8 +174,6 @@
title="选择商品分类" title="选择商品分类"
@confirm="treeConfirm" @confirm="treeConfirm"
@cancel="treeCancel" @cancel="treeCancel"
@handerScrolltolower="handerScrolltolower"
@handleRefresh="handleRefresh"
></tki-tree> ></tki-tree>
<u-popup <u-popup
@ -355,12 +353,10 @@ export default {
if (item) { if (item) {
const findCategory = (categories, targetId) => { const findCategory = (categories, targetId) => {
for (const category of categories) { for (const category of categories) {
// Check current category
if (category.category_id === targetId) { if (category.category_id === targetId) {
return category; return category;
} }
// Check subcategories if they exist
if (category.children && category.children.length > 0) { if (category.children && category.children.length > 0) {
const foundInSub = findCategory(category.children, targetId); const foundInSub = findCategory(category.children, targetId);
if (foundInSub) return foundInSub; if (foundInSub) return foundInSub;
@ -389,12 +385,10 @@ export default {
const findCategory = (categories, targetId) => { const findCategory = (categories, targetId) => {
for (const category of categories) { for (const category of categories) {
// Check current category
if (category.category_id === targetId) { if (category.category_id === targetId) {
return category; return category;
} }
// Check subcategories if they exist
if (category.children && category.children.length > 0) { if (category.children && category.children.length > 0) {
const foundInSub = findCategory(category.children, targetId); const foundInSub = findCategory(category.children, targetId);
if (foundInSub) return foundInSub; if (foundInSub) return foundInSub;
@ -428,33 +422,39 @@ export default {
}; };
this.showAddAndEditPopup = true; this.showAddAndEditPopup = true;
}, },
async handerAffirmAddAndEdit() { handerAffirmAddAndEdit() {
let params = this.form; let params = this.form;
let res = await UpdateSpecification(params); this.$refs.uForm.validate().then(async () => {
if (res && res.status == 200) { let res = await UpdateSpecification(params);
this.$refs.uToast.show({ if (res && res.status == 200) {
message: this.isAdd ? "添加成功" : "修改成功", this.$refs.uToast.show({
type: "succeed", message: this.isAdd ? "添加成功" : "修改成功",
duration: 1000, type: "succeed",
}); duration: 1000,
this.showAddAndEditPopup = false; });
this.showAddAndEditPopup = false;
if (this.isAdd) { //
this.specificationList.push(this.form); let params = {
this.specificationList.sort((a, b) => a.spec_order - b.spec_order); pageSize: 9999,
pageNum: 1,
};
let res = await GetSpecificationList(params);
if (res && res.status == 200) {
this.specificationList = res.data.items;
}
this.form = {
spec_name: "",
spec_order: "",
spec_category_id: 0,
spec_format: "text",
spec_category_name: "",
};
} }
});
// this.specificationList =
this.form = {
spec_name: "",
spec_order: "",
spec_category_id: 0,
spec_format: "text",
spec_category_name: "",
};
}
}, },
async handerDelectClassify() { async handerDelectClassify() {
let params = { let params = {

View File

@ -1,33 +1,637 @@
<template> <template>
<view class="typeManagement-container"> <view class="typeManagement-container">
<view class="typeManagement-content">
<view class="no-commodity-type" v-if="typeManageList.length <= 0">
<view class="no-commodity-type-bg"></view>
<view class="no-commodity-type-tips">暂无商品类型</view>
</view>
<scroll-view
v-if="typeManageList.length > 0"
scroll-y
class="typeManagement-scroll"
:show-scrollbar="false"
@scrolltolower="handerScrolltolower"
refresher-enabled
:refresher-triggered="isRefreshing"
@refresherrefresh="handleRefresh"
>
<view
class="typeManagement-item"
v-for="(item, index) of typeManageList"
:key="index"
>
<view class="typeManagement-info">
<view class="type-name">{{ item.type_name }}</view>
<view class="type-catergory-name">
{{ item.type_category_name }}
</view>
</view>
<view class="specification-btn-content">
<u-icon
class="u-icon-jianhao"
custom-prefix="custom-icon-jianhao custom-icon"
size="20"
@click="handerShowDelectPopup(item)"
></u-icon>
<u-icon
class="u-icon-bianji"
@click="handerAddAndEditPopup(false, item)"
custom-prefix="custom-icon-icon_519 custom-icon"
size="24"
></u-icon>
</view>
</view>
<view
class="m-loading-box"
v-if="typeManageList.length > 0 && !isNotypeManageData"
>
<block v-if="loadingDownTypeManageData">
<view class="u-loadmore">
<view class="u-loading"></view>
<text class="u-loadmore-tips">正在加载...</text>
</view>
</block>
<block v-else>
<view class="u-loadmore u-loadmore-line">
<text class="u-loadmore-tips">没有更多商品类型了 ~</text>
</view>
</block>
</view>
</scroll-view>
</view>
<view class="typeManagement-bottom">
<view class="bottom-list">
<u-button
class="bottom-btn-item"
:hairline="true"
:plain="true"
shape="circle"
@click="handerAddAndEditPopup(true)"
>
<u-icon class="bottom-icon" name="plus-circle"></u-icon>
新建商品类型
</u-button>
</view>
</view>
<u-popup
class="add-popup"
zIndex="10077"
:show="showAddAndEditPopup"
mode="center"
>
<view class="add-popup-content">
<view class="add-popup-title">
{{ isAdd ? "添加商品类型" : "编辑商品类型" }}
</view>
<u--form
labelPosition="left"
:model="form"
ref="uForm"
label-width="100"
:rules="rules"
>
<u-form-item
label="类型名称"
prop="type_name"
class="form-item"
required
>
<u--textarea
class="u-textarea"
v-model="form.type_name"
count
autoHeight
maxlength="20"
placeholder="请输入类型名称"
></u--textarea>
</u-form-item>
<u-form-item
label="商品分类"
prop="type_category_name"
class="form-item"
@click="hadnerShowClassifyListPopup()"
>
<u--input
v-model="form.type_category_name"
disabled
disabledColor="#ffffff"
placeholder="请选择商品分类"
border="none"
@click="hideKeyboard()"
></u--input>
<u-icon slot="right" name="arrow-right"></u-icon>
</u-form-item>
<u-form-item
label="商品规格"
prop="type_spec_names"
class="form-item"
@click="hadnerShowSpecificationPopup()"
>
<u--textarea
class="u-textarea"
v-model="form.type_spec_names"
autoHeight
disabled
disabledColor="#ffffff"
placeholder="请选择类型名称"
></u--textarea>
<u-icon slot="right" name="arrow-right"></u-icon>
</u-form-item>
</u--form>
<view class="popup-btn-list">
<u-button
class="btn-item"
:hairline="true"
:plain="true"
shape="circle"
@click="showAddAndEditPopup = false"
>
取消
</u-button>
<u-button
class="btn-item btn-item-2"
:hairline="true"
:plain="true"
shape="circle"
@click="handerAffirmAddAndEdit"
>
{{ isAdd ? "添加" : "修改" }}
</u-button>
</view>
</view>
</u-popup>
<u-popup
class="specification-popup"
zIndex="10080"
:show="showSpecificationPopup"
mode="bottom"
closeable
@close="handerCloseSpecificationPopup"
>
<view class="specification-popup-content">
<view class="specification-popup-title">选择规格</view>
<scroll-view
v-if="typeManageList.length > 0"
scroll-y
class="specification-type-search-scroll"
:show-scrollbar="false"
:style="{
maxHeight: allSpecificationList.length >= 5 ? '500px' : 'none',
overflowY: allSpecificationList.length >= 5 ? 'auto' : 'visible',
}"
>
<view
class="specification-item"
v-for="(item, index) in allSpecificationList"
:key="index"
>
<view class="specification-name">分类名称{{ item.name }}</view>
<u-checkbox-group
class="specification-checkbox-gourp"
v-model="item.checkboxList"
@change="checkboxChange"
>
<u-checkbox
class="specification-checkbox-item"
v-for="(group, index2) of item.list"
:key="index2"
:customStyle="{ marginBottom: '8px' }"
:label="group.spec_name"
:name="group.spec_id"
></u-checkbox>
</u-checkbox-group>
</view>
</scroll-view>
<view class="popup-btn-list">
<u-button
class="btn-item"
:hairline="true"
:plain="true"
shape="circle"
@click="handerCloseSpecificationPopup"
>
取消
</u-button>
<u-button
class="btn-item btn-item-2"
:hairline="true"
:plain="true"
shape="circle"
@click="handerAffirmSpecification"
>
确认
</u-button>
</view>
</view>
</u-popup>
<u-popup
class="affirm-popup"
zIndex="10077"
:show="showDelectPopup"
mode="center"
>
<view class="affirm-popup-content">
<view class="affirm-popup-title">
确认删除"{{ currDelectItem.type_name }}"商品类型?
</view>
<view class="popup-btn-list">
<u-button
class="btn-item"
:hairline="true"
:plain="true"
shape="circle"
@click="showDelectPopup = false"
>
取消
</u-button>
<u-button
class="btn-item btn-item-2"
:hairline="true"
:plain="true"
shape="circle"
@click="handerDelectType"
>
确认
</u-button>
</view>
</view>
</u-popup>
<tki-tree
ref="tkitree"
:range="classifyList"
:foldAll="true"
rangeKey="label"
idKey="id"
title="选择商品分类"
@confirm="treeConfirm"
@cancel="treeCancel"
></tki-tree>
<u-toast ref="uToast"></u-toast>
</view> </view>
</template> </template>
<script> <script>
import {
GetTypeManageList,
UpdateType,
DelectType,
GetAllSpecification,
} from "@/api/warehouse/typeManage";
import { GetProductCategoryTree } from "@/api/warehouse/productList";
import tkiTree from "./components/tree/tree";
export default { export default {
name: "typeManagement", name: "typeManagement",
components: { components: {
tkiTree,
}, },
data() { data() {
return { return {
isRefreshing: false,
pageNum: 1,
pageSize: 20,
isNotypeManageData: false,
isNoDownTypeManageData: false,
isNotypeManageData: false,
loadingDownTypeManageData: false,
loding: false,
isAdd: false,
showSpecificationPopup: false,
showAddAndEditPopup: false,
showDelectPopup: false,
classifyList: [],
typeManageList: [],
allSpecificationInfo: {},
allSpecificationList: [],
checkboxList: [],
specsCheckboxList: [],
currDelectItem: {},
form: {
type_name: "",
type_category_id: "",
type_category_name: "",
type_spec_ids: "",
type_spec_names: "",
type_is_draft: 0,
type_id: "",
store_id: "",
type_is_param: 0,
type_brand_ids: "",
type_buildin: 0,
type_is_assist: 0,
type_is_draft: 0,
type_is_entity: 0,
type_is_brand: 0,
store_name: "",
type_remark: "",
type_assist_ids: "",
},
rules: {
type_name: [
{
required: true,
message: "请输入商品类型名称",
//
trigger: ["change", "blur"],
},
],
},
}; };
}, },
computed: {}, computed: {},
onLoad: function (options) {}, onLoad: function (options) {},
onReady() {}, onReady() {},
onShow() { onShow() {
this.getProductCategoryTree();
this.getAllSpecification();
this.getTypeManageList();
}, },
mounted() {},
methods: { methods: {
async getProductCategoryTree() {
let res = await GetProductCategoryTree();
if (res && res.status == 200) {
this.classifyList = res.data;
}
},
async getAllSpecification() {
let res = await GetAllSpecification();
if (res && res.status == 200) {
this.allSpecificationInfo = res.data;
if (
this.allSpecificationInfo &&
Object.keys(this.allSpecificationInfo).length > 0
) {
this.allSpecificationList = Object.entries(
this.allSpecificationInfo
).map(([key, value]) => ({
key, // include the original key if needed
list: value,
name: key,
checkboxList: [],
}));
}
console.log(this.allSpecificationList);
}
},
findLabelInClassifyList(classifyList, targetId) {
for (const category of classifyList) {
if (category.id === targetId) {
return category.label;
}
if (category.children && category.children.length > 0) {
const foundLabel = this.findLabelInClassifyList(
category.children,
targetId
);
if (foundLabel) return foundLabel;
}
}
return "未分类"; //
},
//
flatten(arr) {
return arr.reduce((result, item) => {
return result.concat(Array.isArray(item) ? this.flatten(item) : item);
}, []);
},
//
formaSpecification(specIds) {
const list = this.flatten(Object.values(this.allSpecificationInfo));
const selects = list.filter((item) => specIds.includes(item.spec_id));
this.form.type_spec_names = selects
.map((item) => {
return item.spec_name;
})
.join(", ");
this.form.type_spec_ids = selects
.map((item) => {
return item.spec_id;
})
.join(",");
},
handerAddAndEditPopup(isAdd, item) {
this.isAdd = isAdd;
if (isAdd) {
this.form = {
type_name: "",
type_category_name: "",
type_category_id: "",
type_spec_ids: "",
type_spec_names: "",
type_is_draft: 0,
};
} else {
this.form = JSON.parse(JSON.stringify(item));
if (this.form.type_spec_ids.length > 0) {
this.formaSpecification(
this.form.type_spec_ids.split(",").map(Number)
);
if (this.allSpecificationList.length > 0) {
const updatedData = this.updateCheckboxList(
this.allSpecificationList,
this.form.type_spec_ids.split(",").map(Number)
);
}
}
}
this.showAddAndEditPopup = true;
},
updateCheckboxList(data, ids) {
//
return data.map((item) => {
// listspec_idids
const matchedIds = item.list
.filter((spec) => ids.includes(spec.spec_id))
.map((spec) => spec.spec_id);
// checkboxList
if (matchedIds.length > 0) {
return {
...item,
checkboxList: [...new Set([...item.checkboxList, ...matchedIds])],
};
}
return item;
});
},
async getTypeManageList() {
if (!this.loadingDownTypeManageData) {
this.loding = true;
}
if (this.isRefreshing) {
this.pageNum = 1;
this.isNotypeManageData = false;
this.isNoDownTypeManageData = false;
}
let params = {
pageNum: this.pageNum,
pageSize: this.pageSize,
};
let res = await GetTypeManageList(params);
if (res && res.status == 200) {
if (this.loadingDownTypeManageData) {
if (res.data.items.length <= 0) {
this.loadingDownTypeManageData = false;
this.isNoDownTypeManageData = true;
return;
} else {
this.typeManageList = [...this.typeManageList, ...res.data.items];
}
} else {
this.typeManageList = res.data.items;
}
if (this.typeManageList.length <= 0) {
this.isNotypeManageData = true;
}
if (this.typeManageList.length > 0) {
// typeManageList type_category_name
this.typeManageList.forEach((item) => {
item.type_category_name = this.findLabelInClassifyList(
this.classifyList,
item.type_category_id
);
});
}
}
this.loadingDownTypeManageData = false;
this.isRefreshing = false;
},
checkboxChange(e) {
console.log(e);
},
hadnerShowClassifyListPopup() {
this.$refs.tkitree._show();
},
handerCloseAddAndEditPopup() {
this.clearallSpecificationCheckBoxList();
this.showAddAndEditPopup = false;
},
hadnerShowSpecificationPopup() {
if (this.form.type_spec_ids.length > 0) {
this.formaSpecification(this.form.type_spec_ids.split(",").map(Number));
if (this.allSpecificationList.length > 0) {
const updatedData = this.updateCheckboxList(
this.allSpecificationList,
this.form.type_spec_ids.split(",").map(Number)
);
this.allSpecificationList = updatedData;
}
}
this.showSpecificationPopup = true;
},
clearallSpecificationCheckBoxList() {
if (this.allSpecificationList.length > 0) {
this.allSpecificationList.forEach((item) => {
item.checkboxList = [];
});
}
console.log(this.allSpecificationList);
},
handerCloseSpecificationPopup() {
this.clearallSpecificationCheckBoxList();
this.showSpecificationPopup = false;
},
handerShowDelectPopup(item) {
this.currDelectItem = item;
this.showDelectPopup = true;
},
async handerDelectType() {
let params = {
type_ids: this.currDelectItem.type_id,
};
let res = await DelectType(params);
this.showDelectPopup = false;
if (res && res.status == 200) {
this.$refs.uToast.show({
message: "删除成功",
type: "succeed",
duration: 1000,
});
this.typeManageList = this.typeManageList.filter(
(item) => item.type_id !== this.currDelectItem.type_id
);
}
},
handerAffirmAddAndEdit() {
var params = {};
if (this.isAdd) {
params = {
type_name: this.form.type_name,
type_category_id: this.form.type_category_id,
type_is_draft: 0,
type_spec_ids: this.form.type_spec_ids,
};
} else {
params = this.form;
}
this.$refs.uForm.validate().then(async () => {
let res = await UpdateType(params);
if (res && res.status == 200) {
this.$refs.uToast.show({
message: this.isAdd ? "添加成功" : "修改成功",
type: "succeed",
duration: 1000,
});
this.clearallSpecificationCheckBoxList();
//
let params = {
pageSize: 9999,
pageNum: 1,
};
let res = await GetTypeManageList(params);
if (res && res.status == 200) {
this.typeManageList = res.data.items;
}
}
this.showAddAndEditPopup = false;
});
},
handerAffirmSpecification() {
const allCheckboxLists = [];
// 使 for
for (let i = 0; i < this.allSpecificationList.length; i++) {
const item = this.allSpecificationList[i];
// checkboxList
allCheckboxLists.push(...item.checkboxList);
}
this.formaSpecification(allCheckboxLists);
this.showSpecificationPopup = false;
},
treeCancel() {
this.$refs.tkitree._hide();
},
treeConfirm(list) {
if (list.length > 1) {
this.form.type_category_name = list[1].label;
this.form.type_category_id = list[1].id;
} else {
this.form.type_category_name = list[0].label;
this.form.type_category_id = list[0].id;
}
},
}, },
}; };
</script> </script>
@ -35,4 +639,269 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
@import "@/styles/variables.scss"; @import "@/styles/variables.scss";
.typeManagement-container {
.tab-hander {
display: flex;
justify-content: space-around;
margin: 40rpx 0;
font-weight: 500;
}
.m-loading-box {
text-align: center;
padding: 40rpx;
color: #aaaa;
font-size: 28rpx;
}
.typeManagement-item {
display: flex;
justify-content: space-around;
margin: 72rpx 0;
.typeManagement-info {
flex: 1;
display: flex;
justify-content: space-around;
.type-name {
max-width: 160rpx;
word-break: break-all; /* 允许在任意字符间断行 */
overflow-wrap: break-word; /* 优先在单词间断行 */
display: -webkit-box;
-webkit-line-clamp: 2; /* 限制最多2行 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis; /* 超出部分显示省略号 */
}
}
.specification-btn-content {
display: flex;
justify-content: space-around;
width: 220rpx;
.u-icon-jianhao {
::v-deep.custom-icon-jianhao {
color: $base-color !important;
}
}
}
}
::v-deep .u-textarea {
&.u-textarea {
padding-left: 0 !important;
}
&.u-textarea--disabled {
background: #fff;
}
}
.specification-popup {
::v-deep.u-popup__content {
border-top-left-radius: 16rpx;
border-top-right-radius: 16rpx;
}
::v-deep.u-fade-enter-to {
z-index: 10079 !important;
}
.specification-popup-content {
.specification-popup-title {
padding: 40rpx;
text-align: center;
font-weight: 500;
}
}
.specification-name {
padding: 12rpx 36rpx;
background: #f8f8f8;
color: #888888;
}
.specification-checkbox-gourp {
display: flex;
flex-wrap: wrap;
padding: 24rpx;
.specification-checkbox-item {
margin-right: 40rpx;
}
}
.popup-btn-list {
display: flex;
margin: 50rpx;
.btn-item {
width: 46%;
height: 80rpx;
border-color: #909193;
&::after {
border: none;
}
}
.btn-item-2 {
background: $base-color;
color: #fff;
border: none;
}
}
}
.affirm-popup {
::v-deep.u-popup__content {
border-radius: 16rpx;
}
::v-deep.u-fade-enter-to {
z-index: 10076 !important;
}
.affirm-popup-content {
width: 600rpx;
.affirm-popup-title {
padding: 40rpx;
text-align: center;
font-weight: 500;
}
.affirm-popup-tips {
padding: 0 60rpx;
font-size: 28rpx;
text-align: center;
}
.popup-btn-list {
display: flex;
margin: 50rpx;
.btn-item {
width: 46%;
height: 80rpx;
border-color: #909193;
&::after {
border: none;
}
}
.btn-item-2 {
background: $base-color;
color: #fff;
border: none;
}
}
}
}
.add-popup {
::v-deep.u-popup__content {
border-radius: 16rpx;
}
::v-deep.u-fade-enter-to {
z-index: 10076 !important;
}
.add-popup-content {
width: 700rpx;
.add-popup-title {
padding: 40rpx;
text-align: center;
font-weight: 500;
}
.u-form {
margin: 0 40rpx;
.form-item {
margin: 28rpx 0;
}
}
.popup-btn-list {
display: flex;
margin: 50rpx;
.btn-item {
width: 46%;
height: 80rpx;
border-color: #909193;
&::after {
border: none;
}
}
.btn-item-2 {
background: $base-color;
color: #fff;
border: none;
}
}
}
}
.typeManagement-bottom {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 172rpx;
background: #fff;
border-top: 2px solid #eeeeee;
z-index: 2;
.bottom-list {
display: flex;
align-items: center;
justify-content: flex-end;
margin-top: 40rpx;
.bottom-btn-item {
margin: 0;
margin-right: 20rpx;
width: 400rpx;
height: 88rpx;
font-size: 32rpx;
border-color: #d2d2d2;
color: #000;
.bottom-icon {
margin-right: 8rpx;
}
&::after {
border: none;
}
}
}
}
.no-commodity-type {
.no-commodity-type-bg {
margin: 50% auto;
margin-bottom: 0;
width: 300rpx;
height: 200rpx;
background-image: url("../../../static/warehouse/no-commodity-type.png");
background-size: 100%;
}
.no-commodity-type-tips {
margin: 80rpx auto;
color: #cccccc;
text-align: center;
}
}
}
</style> </style>

View File

@ -35,19 +35,23 @@ service.interceptors.response.use(
const res = response.data; const res = response.data;
if (res.status == 250) { if (res.status == 250) {
uni.showToast({ uni.$u.toast(`提示:${res.msg}`);
title: `提示${res.msg}`,
icon: "error", // uni.showToast({
duration: 1000, // title: `提示${res.msg}`,
}); // icon: "error",
// duration: 2000,
// });
} }
if (res.code == 30) { if (res.code == 30) {
uni.showToast({ uni.$u.toast(`token已经过期需要重新登录`);
title: `token已经过期需要重新登录`,
icon: "error", // uni.showToast({
duration: 1000, // title: `token已经过期需要重新登录`,
}); // icon: "error",
// duration: 1000,
// });
$store.dispatch("user/LoginOut", true); $store.dispatch("user/LoginOut", true);