feat: 商品SKU

feature/task1.0.0__0514__ch
向文可 3 years ago
parent c6f5ddb3f9
commit 4c4e1972de

@ -1,2 +1,2 @@
VITE_BASE_URL=http://k8s-horse-gateway.mashibing.cn/
VITE_BASE_URL=https://k8s-horse-gateway.mashibing.cn/
VITE_REQUEST_TIMEOUT=20000

@ -50,7 +50,7 @@ export const createAttrs = (data) => {
export const updateAttrs = (data) => {
return request({
url: '/mall/product/admin/productAttributeGroup/' + data.id,
method: 'post',
method: 'put',
data,
});
};
@ -78,7 +78,7 @@ export const createAttrsValue = (data) => {
export const updateAttrsValue = (data) => {
return request({
url: '/mall/product/admin/productAttribute/' + data.id,
method: 'post',
method: 'put',
data,
});
};

@ -51,8 +51,7 @@ const actions = {
return res;
},
save: async ({ dispatch }, data) => {
let save = data.id ? api.update : api.create;
let res = await save(data);
let res = await api.create(data);
if (res) {
ElMessage.success('保存成功');
dispatch('search');

@ -28,7 +28,7 @@ const actions = {
} else {
try {
await ElMessageBox.confirm('数据删除后无法恢复,确定要删除吗?', '危险操作');
let res = await api.remove({ id: ids.join(',') });
let res = await api.removeAttrs({ id: ids.join(',') });
if (res) {
ElMessage.success('删除成功');
dispatch('search');

@ -0,0 +1,49 @@
import * as api from '@/api/sales/product.js';
import { ElMessage, ElMessageBox } from '@/plugins/element-plus';
const state = () => ({});
const getters = {};
const mutations = {};
const actions = {
search: async (context, id) => {
let res = await api.searchAttrsValue(id);
if (!res) {
ElMessage.error('查询商品属性分组列表失败');
}
return res || [];
},
save: async ({ dispatch }, data) => {
let save = data.id ? api.updateAttrsValue : api.createAttrsValue;
let res = await save(data);
if (res) {
ElMessage.success('保存成功');
dispatch('search');
} else {
ElMessage.error('保存失败');
}
return res;
},
remove: async ({ dispatch }, ids) => {
if (!ids.length) {
ElMessage.warning('请选择要删除的数据');
} else {
try {
await ElMessageBox.confirm('数据删除后无法恢复,确定要删除吗?', '危险操作');
let res = await api.removeAttrsValue({ id: ids.join(',') });
if (res) {
ElMessage.success('删除成功');
dispatch('search');
} else {
ElMessage.error('删除失败');
}
} catch (e) {
console.info('取消删除', e);
}
}
},
};
export default {
state,
getters,
mutations,
actions,
};

@ -1,10 +1,9 @@
import * as api from '@/api/sales/category.js';
import * as categoryAPI from '@/api/sales/category.js';
import * as api from '@/api/sales/product.js';
import { ElMessage } from '@/plugins/element-plus';
const state = () => ({
code: 'ProductPicker',
condition: {
orderIs: [],
},
condition: {},
list: [],
total: 0,
summary: [],
@ -23,9 +22,10 @@ const mutations = {
setOpts: (state, data) => (state.opts = data),
};
const actions = {
search: async ({ state, commit }) => {
let res = await api.search(state.condition);
commit('setList', res);
search: async ({ state, commit, rootGetters }) => {
let res = await api.search({ ...rootGetters['local/page'](state.code), ...state.condition });
commit('setList', res?.records || []);
commit('setTotal', res?.total || 0);
if (!res) {
ElMessage.error('查询商品列表失败');
}
@ -35,7 +35,7 @@ const actions = {
commit('setOpts', {
...state.opts,
init: true,
category: await api.search(),
category: await categoryAPI.search({ pageIndex: 1, length: 9999 }),
});
},
};

@ -52,6 +52,8 @@
phone: '',
password: '',
verifyCode: '',
systemId: 1,
clientId: 2,
});
const rules = reactive({

@ -80,6 +80,7 @@
await store.dispatch('limitActivity/search');
loading.value = false;
};
onActivated(handleSearch);
/* 表单 */
const refsForm = ref(null);
@ -191,10 +192,18 @@
slots: {
default: ({ row }) => (
<div>
<ElButton type="text" onClick={() => handleUpdateTime(row)}>
<ElButton
disabled={row.activityState !== 1}
type="text"
onClick={() => handleUpdateTime(row)}
>
设置时段
</ElButton>
<ElButton type="text" onClick={() => handleUpdateProduct(row)}>
<ElButton
type="text"
disabled={row.activityState !== 1}
onClick={() => handleUpdateProduct(row)}
>
设置商品
</ElButton>
<ElButton type="text" disabled={row.activityState !== 1} onClick={() => handleCreate(row)}>

@ -33,7 +33,7 @@
</el-form>
</template>
</TableList>
<ProductPicker ref="refsPicker" />
<ProductPicker ref="refsPicker" @pick="handlePick" />
</div>
</template>
@ -47,9 +47,6 @@
const list = computed(() => store.state.limitProduct.list);
const total = computed(() => store.state.limitProduct.total);
const opts = computed(() => store.state.limitProduct.opts);
if (!unref(opts).init) {
store.dispatch('limitProduct/load', route.params.id);
}
/* 查询订单 */
const state = reactive({
@ -68,6 +65,7 @@
};
const handleSearch = async () => {
loading.value = true;
await store.dispatch('limitProduct/load', route.params.id);
await store.dispatch('limitProduct/search');
loading.value = false;
};
@ -92,6 +90,7 @@
},
{ deep: true }
);
onActivated(handleSearch);
const refsPicker = ref(null);
const handleCreate = async () => {
@ -106,6 +105,14 @@
const handleSku = (row) => {
console.info(row);
};
const handlePick = async (rows) => {
loading.value = true;
await store.dispatch('limitProduct/save', {
activityTimeId: state.condition.activityTimeId,
productId: rows.map((item) => item.id),
});
loading.value = false;
};
/* 列表配置 */
const config = reactive({

@ -75,7 +75,9 @@
};
//
const handlePick = async (rows) => {
console.info(rows);
loading.value = true;
await store.dispatch('recommendProduct/save', { productIdList: rows.map((item) => item.id) });
loading.value = false;
};
/* 操作 */

@ -1,70 +1,72 @@
<template>
<div v-loading="loading" class="step-container">
<el-scrollbar class="step-content">
<ul class="flex">
<li class="flex">
<span class="label">属性规格:</span>
<el-form>
<el-form-item label="属性规格">
<el-input v-model="skuName">
<template #append>
<el-button @click="handleAddSku"><el-icon name="Check" /></el-button>
</template>
</el-input>
</li>
<li v-for="(sku, i) in form.skus" :key="i" class="flex">
<div class="left">
<span class="label">{{ sku.name }}:</span>
<el-tag
v-for="(item, j) in sku.values"
:key="i + '' + j"
closable
@close="handleDelValue(sku, j)"
>
{{ item }}
</el-tag>
<el-input v-model="skuValue[i]">
<template #append>
<el-button @click="handleAddValue(sku, i)"><el-icon name="Check" /></el-button>
</template>
</el-input>
</el-form-item>
<el-form-item v-for="(sku, i) in form.skus" :key="i" :label="sku.name">
<div class="flex">
<div class="left">
<el-tag
v-for="(item, j) in sku.values"
:key="i + '' + j"
closable
@close="handleDelValue(sku, j)"
>
{{ item.name }}
</el-tag>
<el-input v-model="skuValue[i]">
<template #append>
<el-button @click="handleAddValue(sku, i)"><el-icon name="Check" /></el-button>
</template>
</el-input>
</div>
<div class="right">
<el-button :disabled="i === 0" type="text" @click="handleMove(i, i - 1)">上移</el-button>
<el-button :disabled="i === form.skus.length - 1" type="text" @click="handleMove(i, i + 1)">
下移
</el-button>
<el-button type="text" @click="handleDelSku(i)"></el-button>
</div>
</div>
<div class="right">
<el-button :disabled="i === 0" type="text" @click="handleMove(i, i - 1)">上移</el-button>
<el-button :disabled="i === form.skus.length - 1" type="text" @click="handleMove(i, i + 1)">
下移
</el-button>
<el-button type="text" @click="handleDelSku(i)"></el-button>
</div>
</li>
</ul>
</el-form-item>
</el-form>
<el-table border :data="form.skuInfos" style="width: 100%">
<el-table-column
v-for="(sku, index) in form.skus"
:key="index"
align="center"
header-align="center"
:label="sku.name"
:prop="'attr' + index"
:prop="sku.id + ''"
/>
<el-table-column label="售价(元)" prop="sales">
<el-table-column align="center" header-align="center" label="售价(元)" prop="sales" width="160px">
<template #default="{ row }">
<el-input-number v-model="row.sales" />
</template>
</el-table-column>
<el-table-column label="商品库存" prop="inventory">
<el-table-column align="center" header-align="center" label="商品库存" prop="inventory" width="160px">
<template #default="{ row }">
<el-input-number v-model="row.inventory" />
</template>
</el-table-column>
<el-table-column label="成本价(元)" prop="price">
<el-table-column align="center" header-align="center" label="成本价(元)" prop="price" width="160px">
<template #default="{ row }">
<el-input-number v-model="row.price" />
</template>
</el-table-column>
<el-table-column label="库存预警值" prop="warn">
<el-table-column align="center" header-align="center" label="库存预警值" prop="warn" width="160px">
<template #default="{ row }">
<el-input-number v-model="row.warn" />
</template>
</el-table-column>
<el-table-column label="SKU编号" prop="no" />
<el-table-column label="启用" prop="enabled">
<el-table-column align="center" header-align="center" label="SKU编号" min-width="100px" prop="no" />
<el-table-column align="center" header-align="center" label="启用" prop="enabled" width="80px">
<template #default="{ row }">
<el-switch v-model="row.enabled" />
</template>
@ -103,7 +105,15 @@
const id = route.params.id;
if (id && id !== state.form.id) {
state.form.id = id;
state.form.skus = await store.dispatch('productAttrsGroup/search', id);
let res = await store.dispatch('productAttrsGroup/search', id);
let arr = [];
res.forEach((item) => {
arr.push(
store.dispatch('productAttrsValue/search', item.id).then((res) => (item.values = res))
);
});
await Promise.all(arr);
state.form.skus = res;
}
};
onActivated(handleLoad);
@ -114,8 +124,8 @@
(info, sku) => {
let res = [];
info.forEach((row) => {
sku.values.forEach((value) => {
res.push({ ...row, ['attr' + state.form.skus.indexOf(sku)]: value });
(sku.values || []).forEach((value) => {
res.push({ ...row, [sku.id + '']: value.name });
});
});
return res;
@ -194,31 +204,21 @@
.flex {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
.el-input {
width: 160px;
}
li {
align-items: center;
&:not(:first-child) {
justify-content: space-between;
.el-tag {
+ .el-tag {
margin-left: @layout-space;
}
.el-tag {
+ .el-tag {
margin-left: @layout-space;
}
+ .el-input {
margin-left: @layout-space;
}
}
+ li {
margin-top: @layout-space;
+ .el-input {
margin-left: @layout-space;
}
}
}
.label {
word-break: keep-all;
margin-right: @layout-space;
.el-input-number {
width: 100%;
}
}
.step-footer {

@ -16,9 +16,9 @@
<el-form-item label="商品名称" prop="name">
<el-input v-model="state.condition.name" />
</el-form-item>
<el-form-item label="商品分类" prop="category">
<el-form-item label="商品分类" prop="categoryId">
<el-select
v-model="state.condition.category"
v-model="state.condition.categoryId"
:config="{ label: 'name', value: 'id' }"
:opts="opts.category"
/>
@ -51,8 +51,9 @@
const state = reactive({
condition: {
name: null,
category: null,
categoryId: null,
},
picked: null,
});
watch(
() => state.condition,
@ -64,7 +65,7 @@
const handleReset = () => {
state.condition = {
name: null,
category: null,
categoryId: null,
};
};
const handleSearch = async () => {
@ -77,6 +78,7 @@
//
const handleShow = () => {
visible.value = true;
handleSearch();
};
//
const handleClose = () => {
@ -85,6 +87,7 @@
//
const handlePick = async () => {
emits('pick', [...unref(refsTable).selection]);
handleClose();
};
defineExpose({
show: handleShow,
@ -108,7 +111,7 @@
label: '商品图片',
minWidth: 100,
slots: {
default: ({ row }) => <ElImage src={row.picture} alt={row.name} height="100px" />,
default: ({ row }) => <ElImage src={row.mainPicture} alt={row.name} height="100px" />,
},
},
{
@ -118,7 +121,7 @@
},
{
label: '商品价格',
prop: 'price',
prop: 'startingPrice',
minWidth: 160,
},
],

@ -22,7 +22,7 @@ export default (configEnv) => {
'/api': {
// target: 'http://192.168.10.109:8090/', // 显雨
// target: 'http://192.168.10.251:8090', // 高玉
target: 'http://k8s-horse-gateway.mashibing.cn/', // 测试地址
target: 'https://k8s-horse-gateway.mashibing.cn/', // 测试地址
// target: 'https://gateway.mashibing.cn', // 预发地址
// target: 'https://gateway.mashibing.com', // 生产环境
changeOrigin: true,

Loading…
Cancel
Save