gukai 2 years ago
parent 171ea44f27
commit abbf377e7c

@ -1,429 +1,550 @@
<template> <template>
<view v-if="load" class="content p-2 bg-gray-100"> <view v-if="load" class="content p-2 bg-gray-100">
<wd-toast /> <wd-toast />
<wd-form ref="form" :model="model"> <wd-form ref="form" :model="model">
<wd-cell-group border> <wd-cell-group border>
<div class="rounded-md overflow-hidden"> <div class="rounded-md overflow-hidden">
<div class="bg-gray-100 "> <div class="bg-gray-100 ">
<div class="bg-gray-100 rounded mb-2"> <div class="bg-gray-100 rounded mb-2">
<div class="bg-white flex flex-col pt-3 pl-3"> <div class="bg-white flex flex-col pt-3 pl-3">
<div class="name mb-2 text-xs">缩略图</div> <div class="name mb-2 text-xs">缩略图</div>
<yUpload v-model="model.pic_url"></yUpload> <yUpload v-model="model.pic_url"></yUpload>
</div> </div>
</div> </div>
</div> </div>
<div class="bg-gray-100"> <div class="bg-gray-100">
<div class="bg-gray-100 rounded mb-2"> <div class="bg-gray-100 rounded mb-2">
<div class="bg-white flex flex-col pt-3 pl-3"> <div class="bg-white flex flex-col pt-3 pl-3">
<div class="name mb-2 text-xs">商品主图</div> <div class="name mb-2 text-xs">商品主图</div>
<yUpload v-model="model.pic_list" :size="4"></yUpload> <yUpload v-model="model.pic_list" :size="4"></yUpload>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<view class="h-2 bg-gray-100"></view> <view class="h-2 bg-gray-100"></view>
<view class=" p-3 rounded mb-2"> <view class=" p-3 rounded mb-2">
<div class="name mb-2 text-xs">商品名称</div> <div class="name mb-2 text-xs">商品名称</div>
<view class="bg-white px-3 py-1 rounded"> <view class="bg-white px-3 py-1 rounded">
<wd-textarea v-model="model.name" auto-height /> <wd-textarea v-model="model.name" auto-height />
</view> </view>
</view> </view>
<view class="h-2 bg-gray-100"></view> <view class="h-2 bg-gray-100"></view>
<view class=" p-3 rounded mb-2"> <view class=" p-3 rounded mb-2">
<div class="name mb-2 text-xs">商品分类</div> <div class="name mb-2 text-xs">商品分类</div>
<view class="bg-white px-3 py-1 rounded"> <view class="bg-white px-3 py-1 rounded">
<wd-checkbox-group v-model="model.classify_list"> <wd-checkbox-group v-model="model.classify_list">
<view class="flex overflow-auto flex-wrap"> <view class="flex overflow-auto flex-wrap">
<div class="grid grid-cols-3"> <div class="grid grid-cols-3">
<wd-checkbox style="width: 100%;" shape="button" v-for="cat of classifyList" <wd-checkbox style="width: 100%;" shape="button" v-for="cat of classifyList"
:modelValue="JSON.stringify(cat.parentIds)"> :modelValue="JSON.stringify(cat.parentIds)">
{{ cat.name }} {{ cat.name }}
</wd-checkbox> </wd-checkbox>
</div> </div>
</view> </view>
</wd-checkbox-group> </wd-checkbox-group>
</view> </view>
</view> </view>
<view class="h-2 bg-gray-100"></view> <view class="h-2 bg-gray-100"></view>
<view class="bg-gray-100 rounded mb-2"> <view class="bg-gray-100 rounded mb-2">
<div class="p-2 bg-white"> <div class="p-2 bg-white">
<div class="name mb-2 text-xs">服务内容</div> <div class="name mb-2 text-xs">服务内容</div>
<view class="bg-white px-3 py-1 rounded"> <view class="bg-white px-3 py-1 rounded">
<wd-input type="text" v-model="model.server_project" placeholder="例子: 正品保障,极速发货,7天退换货。多个请使用英文逗号“,”分隔" /> <wd-input type="text" v-model="model.server_project"
</view> placeholder="例子: 正品保障,极速发货,7天退换货。多个请使用英文逗号“,”分隔" />
</div> </view>
</view> </div>
</view>
<view class="h-2 bg-gray-100"></view>
<view class="h-2 bg-gray-100"></view>
<view class=" p-3 rounded mb-2">
<div class="name mb-2 text-xs">商品重量</div> <view class=" p-3 rounded mb-2">
<view class="bg-white px-3 py-2 rounded flex items-center mb-4"> <div class="name mb-2 text-xs">商品重量</div>
<wd-input-number v-model="model.weight" /> <view class="bg-white px-3 py-2 rounded flex items-center mb-4">
<view class="text-xs from-neutral-300 ml-4">千克</view> <wd-input-number v-model="model.weight" />
</view> <view class="text-xs from-neutral-300 ml-4">千克</view>
</view>
<wd-picker v-if="freightRules.length" class="w-full" :columns="freightRules" label="运费模板"
v-model="model.freight_id" /> <wd-picker v-if="freightRules.length" class="w-full" :columns="freightRules" label="运费模板"
v-model="model.freight_id" />
</view>
</view>
<view class="h-2 bg-gray-100"></view>
<view class="h-2 bg-gray-100"></view>
<view class="p-3 rounded mb-2">
<div class="name mb-2 text-xs">商品状态</div> <view class="p-3 rounded mb-2">
<view class="bg-white px-3 py-2 rounded flex items-center"> <div class="name mb-2 text-xs">商品状态</div>
<wd-switch v-model="model.status" :active-value="1" :inactive-value="0" /> <view class="bg-white px-3 py-2 rounded flex items-center">
</view> <wd-switch v-model="model.status" :active-value="1" :inactive-value="0" />
</view> </view>
</view>
<view class="h-2 bg-gray-100"></view>
<view class="h-2 bg-gray-100"></view>
<view class="p-3 rounded mb-2">
<div class="name mb-2 text-xs">规格</div> <view class="p-3 rounded mb-2">
<view class="bg-white px-3 py-2 rounded flex items-center"> <div class="name mb-2 text-xs">规格</div>
<wd-radio-group shape="button" v-model="model.use_sku"> <view class="bg-white px-3 py-2 rounded flex items-center">
<wd-radio :value="0">单规格</wd-radio> <wd-radio-group shape="button" v-model="model.use_sku">
<wd-radio :value="1">多规格</wd-radio> <wd-radio :value="0">单规格</wd-radio>
</wd-radio-group> <wd-radio :value="1">多规格</wd-radio>
</view> </wd-radio-group>
</view> </view>
</view>
<view class="h-2 bg-gray-100"></view>
<view class="h-2 bg-gray-100"></view>
<skuEdit ref="skuEditRef" :skuGroup="skuGroup"
:Pdata="{ skuDefault, skuLibrary, skuGroup, use_sku: model.use_sku }"> <skuEdit ref="skuEditRef" :skuGroup="skuGroup"
</skuEdit> :Pdata="{ skuDefault, skuLibrary, skuGroup, use_sku: model.use_sku }">
</skuEdit>
</wd-cell-group>
<view class="footer mt-4"> <view class="h-2 bg-gray-100"></view>
<wd-button @click="saveGoods" type="primary" size="large" block>保存</wd-button> <view>
</view> <view style="font-size: 36rpx;border-bottom: 1rpx solid #F0F0F0;display: flex;flex-wrap: wrap;align-items: center;">
</wd-form> <view
</view> :style="{
margin: '20rpx',
fontWeight: formats.bold ? '800' : '400'
}"
@click="format('bold', (formats.bold ? false : true))">加粗</view>
<view
:style="{
margin: '20rpx',
fontWeight: formats.italic ? '800' : '400'
}"
@click="format('italic', (formats.italic ? false : true))">斜体</view>
<view
:style="{
margin: '20rpx',
fontWeight: formats.underline ? '800' : '400'
}"
@click="format('underline', (formats.underline ? false : true))">下划线</view>
<view
:style="{
margin: '20rpx',
fontWeight: formats.align == 'center' ? '800' : '400'
}"
@click="format('align', (formats.align == 'center' ? 'left' : 'center'))">居中</view>
<view style="margin: 20rpx;" >
<yUpload :width="40" :height="40" v-model="editor_upload" :size="4"></yUpload>
</view>
</view>
<view style="padding: 10px;">
<editor style="min-height: 70vh;" id="editor" class="ql-container"
placeholder="开始输入..." show-img-size show-img-toolbar
@statuschange="onStatusChange"
show-img-resize :read-only="false" @ready="onEditorReady">
</editor>
</view>
</view>
</wd-cell-group>
<view class="footer mt-4">
<wd-button @click="saveGoods" type="primary" size="large" block>保存</wd-button>
</view>
</wd-form>
</view>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'; import {
import { useToast, useMessage } from '@/uni_modules/wot-design-uni'; ref,
import goods from '@/api/store/goods.js'; watch
import system from '@/api/modules/system.js'; } from 'vue';
import skuEdit from "./components/skuEdit.vue"; import {
import yUpload from "@/components/yUpload/index.vue"; useToast,
import { onLoad, onShow } from "@dcloudio/uni-app"; useMessage
} from '@/uni_modules/wot-design-uni';
/** import goods from '@/api/store/goods.js';
* @typedef {import('@/api/store/goods').GoodsItem} GoodsItem import system from '@/api/modules/system.js';
*/ import skuEdit from "./components/skuEdit.vue";
import yUpload from "@/components/yUpload/index.vue";
/** import {
* @typedef {import('@/api/store/goods').FreightRule} FreightRule onLoad,
*/ onShow
} from "@dcloudio/uni-app";
/**
* @typedef {Object} Sku /**
* @property {string} price - 商品价格 * @typedef {import('@/api/store/goods').GoodsItem} GoodsItem
* @property {string} stock - 商品库存 */
* @property {string} original_price - 商品原价
*/ /**
* @typedef {import('@/api/store/goods').FreightRule} FreightRule
/** */
* @typedef {Object} Model
* @property {number} id - 商品ID /**
* @property {Array<string>} classify_list - 商品分类组 * @typedef {Object} Sku
* @property {string} name - 商品名称 * @property {string} price - 商品价格
* @property {string} keywords - 商品关键词 * @property {string} stock - 商品库存
* @property {number} sort - 商品排序 * @property {string} original_price - 商品原价
* @property {number} status - 商品状态 */
* @property {number} freight_id - 运费规则ID
* @property {number} weight - 商品重量 /**
* @property {string} pic_url - 商品缩略图 * @typedef {Object} Model
* @property {Array<string>} pic_list - 商品图组 * @property {number} id - 商品ID
* @property {string} video - 商品视频 * @property {Array<string>} classify_list - 商品分类组
* @property {string} detail - 图文详情 * @property {string} name - 商品名称
* @property {number} use_sku - 是否使用规格 * @property {string} keywords - 商品关键词
* @property {Array<Sku>} sku_list - 规格信息 * @property {number} sort - 商品排序
* @property {number} is_discount - 是否有折扣 * @property {number} status - 商品状态
* @property {number} discount - 折扣比例小数 * @property {number} freight_id - 运费规则ID
* @property {number} dist_price - 分销价格 * @property {number} weight - 商品重量
* @property {number} limit - 限购 * @property {string} pic_url - 商品缩略图
* @property {number} is_check - 自定义核销佣金 * @property {Array<string>} pic_list - 商品图组
* @property {number} check_type - 佣金类型 * @property {string} video - 商品视频
* @property {number} check_price - 佣金 * @property {string} detail - 图文详情
*/ * @property {number} use_sku - 是否使用规格
* @property {Array<Sku>} sku_list - 规格信息
/** * @property {number} is_discount - 是否有折扣
* @typedef {Object} FileItem * @property {number} discount - 折扣比例小数
* @property {string} pic_url - 图片URL * @property {number} dist_price - 分销价格
*/ * @property {number} limit - 限购
* @property {number} is_check - 自定义核销佣金
/** * @property {number} check_type - 佣金类型
* @typedef {Object} SkuGroup * @property {number} check_price - 佣金
* @property {Array<Sku>} skuDefault - 默认规格 */
* @property {Array<Sku>} skuLibrary - 规格库
* @property {Array<Sku>} skuGroup - 规格组 /**
*/ * @typedef {Object} FileItem
* @property {string} pic_url - 图片URL
/** */
* @typedef {Object} SkuEditRef
* @property {SkuGroup} skuGroup - 规格组 /**
* @property {number} skuDefault - 默认规格 * @typedef {Object} SkuGroup
* @property {number} skuLibrary - 规格库 * @property {Array<Sku>} skuDefault - 默认规格
*/ * @property {Array<Sku>} skuLibrary - 规格库
* @property {Array<Sku>} skuGroup - 规格组
/** @type {Ref<Array<FileItem>>} */ */
const fileList = ref([]);
/**
/** * @typedef {Object} SkuEditRef
* @param {Object} param0 * @property {SkuGroup} skuGroup - 规格组
* @param {FileItem} param0.file - 要删除的文件 * @property {number} skuDefault - 默认规格
* @param {Function} param0.resolve - 删除文件的回调函数 * @property {number} skuLibrary - 规格库
* @param {number} param0.index - 文件索引 */
* @returns {void}
*/ /** @type {Ref<Array<FileItem>>} */
const beforeRemove = ({ file, resolve, index }) => { const fileList = ref([]);
fileList.value.splice(index, 1);
resolve(true); /**
}; * @param {Object} param0
* @param {FileItem} param0.file - 要删除的文件
/** @type {Ref<Model>} */ * @param {Function} param0.resolve - 删除文件的回调函数
const model = ref({ * @param {number} param0.index - 文件索引
classify_list: [], // * @returns {void}
name: "", // */
keywords: "", // const beforeRemove = ({
sort: 1000, file,
status: 0, // resolve,
freight_id: 99999999, // index
weight: 0, // }) => {
pic_url: "", // fileList.value.splice(index, 1);
pic_list: [], // resolve(true);
video: "", // };
detail: "默认", //
use_sku: 0, // 使
sku_list: [], // const editorCtx = ref(null);
is_discount: 0, // const onEditorReady = () => {
discount: 50, // // #ifdef MP-BAIDU
dist_price: 0, // editorCtx.value = requireDynamicLib('editorLib').createEditorContext('editor');
limit: 0, // // #endif
is_check: 0, //
check_type: 0, // // #ifdef APP-PLUS || MP-WEIXIN || H5
check_price: 0, // uni.createSelectorQuery().select('#editor').context((res) => {
}); editorCtx.value = res.context
}).exec()
/** @type {Ref<boolean>} */ // #endif
const load = ref(false); }
const formats = ref({});
/** @type {Ref<SkuGroup>} */ const onStatusChange = (e) => {
const skuGroup = ref([]); formats.value = e.detail
/** @type {Ref<Array<GoodsItem>>} */ }
const skuDefault = ref([]);
/** @type {Ref<Array<GoodsItem>>} */ const format = (name, value) => {
const skuLibrary = ref([]); editorCtx.value.format(name, value)
}
/** @type {Ref<SkuEditRef>} */
const skuEditRef = ref(null); const editor_upload = ref([]);
watch(editor_upload, (newX) => {
/** @type {Ref<Array<FreightRule>>} */ if (newX && newX.length > 0) {
const freightRules = ref([{ newX.forEach(item => {
label: "默认", editorCtx.value.insertImage({
value: "99999999" src: item,
}]); alt: '图像',
success: function() {
onLoad(async (e) => { console.log('insert image success')
model.value.id = e.id; }
})
/** })
* 获取运费规则
* @returns {void} editor_upload.value = []
*/ }
const GetfreightRules = () => { // console.log(newX, `newX is`)
goods.freightRules().then(res => { })
freightRules.value = [
...freightRules.value,
...res.data.rows.map(item => { /** @type {Ref<Model>} */
return { const model = ref({
label: item.name, classify_list: [], //
value: item.id name: "", //
}; keywords: "", //
}) sort: 1000,
]; status: 0, //
}); freight_id: 99999999, //
}; weight: 0, //
pic_url: "", //
GetfreightRules(); pic_list: [], //
video: "", //
if (e.id > 0) { detail: "默认", //
const res = await goods.goodsItem({ use_sku: 0, // 使
id: e.id sku_list: [], //
}); is_discount: 0, //
discount: 50, //
model.value = res.data; dist_price: 0, //
model.value.pic_list = res.data.pic_list.map(({ pic_url }) => pic_url); limit: 0, //
is_check: 0, //
if (!model.value.freight_id) { check_type: 0, //
model.value.freight_id = 99999999; check_price: 0, //
} });
model.value.classify_list = model.value.classify_list.map(item => JSON.stringify(item));
/** @type {Ref<boolean>} */
if (res.data.use_sku == 0) { const load = ref(false);
skuDefault.value = res.data.goods_sku;
} else { /** @type {Ref<SkuGroup>} */
skuLibrary.value = res.data.goods_sku; const skuGroup = ref([]);
} /** @type {Ref<Array<GoodsItem>>} */
skuGroup.value = res.data.sku_group ? res.data.sku_group : []; const skuDefault = ref([]);
load.value = true; /** @type {Ref<Array<GoodsItem>>} */
} else { const skuLibrary = ref([]);
load.value = true;
} /** @type {Ref<SkuEditRef>} */
}); const skuEditRef = ref(null);
/** /** @type {Ref<Array<FreightRule>>} */
* 保存商品信息 const freightRules = ref([{
* @returns {Promise<void>} label: "默认",
*/ value: "99999999"
const saveGoods = async () => { }]);
//
if (model.value.classify_list.length === 0) { onLoad(async (e) => {
return uni.showToast({ model.value.id = e.id;
title: '请选择商品分类!',
icon: 'none' /**
}); * 获取运费规则
} * @returns {void}
*/
// const GetfreightRules = () => {
if (model.value.name.trim() === "") { goods.freightRules().then(res => {
return uni.showToast({ freightRules.value = [
title: '请输入商品名称!', ...freightRules.value,
icon: 'none' ...res.data.rows.map(item => {
}); return {
} label: item.name,
value: item.id
// };
if (model.value.weight < 0) { })
return uni.showToast({ ];
title: '请输入合法的商品重量!', });
icon: 'none' };
});
} GetfreightRules();
// if (e.id > 0) {
if (String(model.value.pic_url).trim() === "") { const res = await goods.goodsItem({
return uni.showToast({ id: e.id
title: '请上传商品缩略图!', });
icon: 'none'
}); model.value = res.data;
} model.value.pic_list = res.data.pic_list.map(({
pic_url
// }) => pic_url);
if (model.value.pic_list.length === 0) {
return uni.showToast({ if (!model.value.freight_id) {
title: '请上传商品图片组!', model.value.freight_id = 99999999;
icon: 'none' }
}); model.value.classify_list = model.value.classify_list.map(item => JSON.stringify(item));
}
if (res.data.use_sku == 0) {
model.value.sku_group = []; skuDefault.value = res.data.goods_sku;
model.value.sku_list = []; } else {
model.value.dist_price = Number.parseInt(model.value.dist_price); skuLibrary.value = res.data.goods_sku;
}
if (model.value.use_sku == 1) { skuGroup.value = res.data.sku_group ? res.data.sku_group : [];
model.value.sku_group = skuEditRef.value.skuGroup;
model.value.sku_list = skuEditRef.value.skuLibrary; setTimeout(() => {
} else { editorCtx.value.setContents({
model.value.sku_list = skuEditRef.value.skuDefault; html: res.data.detail
} })
}, 1000)
if (!model.value.sku_list.length) {
uni.showToast({ load.value = true;
title: '规格不完整,请调整!', } else {
icon: 'none' load.value = true;
}); }
} });
if (!model.value.sku_list.every(item => Boolean(item.price !== "" && item.stock !== "" && item.original_price !== ""))) { /**
uni.showToast({ * 保存商品信息
title: '规格不完整,请调整!', * @returns {Promise<void>}
icon: 'none' */
}); const saveGoods = async () => {
//
return; if (model.value.classify_list.length === 0) {
} return uni.showToast({
title: '请选择商品分类!',
let freight_id = model.value.freight_id; icon: 'none'
if (freight_id = 99999999) { });
freight_id = 0; }
}
//
const pic_list = model.value.pic_list.map(item => ({ if (model.value.name.trim() === "") {
pic_url: item return uni.showToast({
})); title: '请输入商品名称!',
icon: 'none'
const classify_list = model.value.classify_list.map(item => JSON.parse(item)); });
}
const res = await goods.goodsEdit({ ...model.value, pic_list, freight_id, classify_list });
//
if (res.code == 0) { if (model.value.weight < 0) {
uni.showToast({ return uni.showToast({
title: '商品信息保存成功!', title: '请输入合法的商品重量!',
icon: 'success' icon: 'none'
}); });
}
uni.navigateBack();
} //
}; if (String(model.value.pic_url).trim() === "") {
return uni.showToast({
/** @type {Ref<Array<Object>>} */ title: '请上传商品缩略图!',
const classifyList = ref([]); icon: 'none'
});
/** }
* 获取商品分类
* @returns {void} //
*/ if (model.value.pic_list.length === 0) {
const getClassify = () => { return uni.showToast({
/** title: '请上传商品图片组!',
* 递归展平商品分类 icon: 'none'
* @param {Array<GoodsItem>} categories - 商品分类数组 });
* @param {Array<number>} [parentIds=[]] - 父级分类ID数组 }
* @returns {Array<Object>} - 展平后的商品分类数组
*/ model.value.sku_group = [];
function flattenCategories(categories, parentIds = []) { model.value.sku_list = [];
let flatCategories = []; model.value.dist_price = Number.parseInt(model.value.dist_price);
for (const category of categories) { if (model.value.use_sku == 1) {
const categoryWithParents = { model.value.sku_group = skuEditRef.value.skuGroup;
...category, model.value.sku_list = skuEditRef.value.skuLibrary;
parentIds: [...parentIds, category.id], } else {
}; model.value.sku_list = skuEditRef.value.skuDefault;
}
flatCategories.push(categoryWithParents);
if (!model.value.sku_list.length) {
if (category.children && category.children.length > 0) { uni.showToast({
flatCategories = flatCategories.concat(flattenCategories(category.children, [...parentIds, category.id])); title: '规格不完整,请调整!',
} icon: 'none'
} });
}
return flatCategories;
} if (!model.value.sku_list.every(item => Boolean(item.price !== "" && item.stock !== "" && item
.original_price !== ""))) {
goods.classify.list().then(res => { uni.showToast({
classifyList.value = flattenCategories(res.data); title: '规格不完整,请调整!',
}); icon: 'none'
}; });
getClassify(); return;
}
let freight_id = model.value.freight_id;
if (freight_id = 99999999) {
freight_id = 0;
}
const pic_list = model.value.pic_list.map(item => ({
pic_url: item
}));
const classify_list = model.value.classify_list.map(item => JSON.parse(item));
let detail = ''
await editorCtx.value.getContents({
success(res) {
detail = res.html
}
})
const res = await goods.goodsEdit({
...model.value,
pic_list,
freight_id,
classify_list,
detail
});
if (res.code == 0) {
uni.showToast({
title: '商品信息保存成功!',
icon: 'success'
});
uni.navigateBack();
}
};
/** @type {Ref<Array<Object>>} */
const classifyList = ref([]);
/**
* 获取商品分类
* @returns {void}
*/
const getClassify = () => {
/**
* 递归展平商品分类
* @param {Array<GoodsItem>} categories - 商品分类数组
* @param {Array<number>} [parentIds=[]] - 父级分类ID数组
* @returns {Array<Object>} - 展平后的商品分类数组
*/
function flattenCategories(categories, parentIds = []) {
let flatCategories = [];
for (const category of categories) {
const categoryWithParents = {
...category,
parentIds: [...parentIds, category.id],
};
flatCategories.push(categoryWithParents);
if (category.children && category.children.length > 0) {
flatCategories = flatCategories.concat(flattenCategories(category.children, [...parentIds, category
.id
]));
}
}
return flatCategories;
}
goods.classify.list().then(res => {
classifyList.value = flattenCategories(res.data);
});
};
getClassify();
</script> </script>
<style lang="scss"></style> <style lang="scss"></style>
Loading…
Cancel
Save