Compare commits

..

2 Commits

Author SHA1 Message Date
d09f988d7c Merge branch 'master' of https://git.van333.cn/CC/ruoyi-vue 2025-08-19 01:45:46 +08:00
07c1a23a89 1 2025-08-19 01:28:04 +08:00
5 changed files with 487 additions and 579 deletions

View File

@@ -1,128 +1,30 @@
import request from '@/utils/request'
// 查询常用商品列表
export function listFavoriteProduct(query) {
return request({
url: '/jarvis/favoriteProduct/list',
method: 'get',
params: query
})
}
// 查询常用商品详细
export function getFavoriteProduct(id) {
return request({
url: '/jarvis/favoriteProduct/' + id,
method: 'get'
})
}
// 新增常用商品
export function addFavoriteProduct(data) {
return request({
url: '/jarvis/favoriteProduct',
method: 'post',
data: data
})
}
// 修改常用商品
export function updateFavoriteProduct(data) {
return request({
url: '/jarvis/favoriteProduct',
method: 'put',
data: data
})
}
// 删除常用商品
export function delFavoriteProduct(id) {
return request({
url: '/jarvis/favoriteProduct/' + id,
method: 'delete'
})
}
// 批量删除常用商品
export function delFavoriteProducts(ids) {
return request({
url: '/jarvis/favoriteProduct/' + ids,
method: 'delete'
})
}
// 添加商品到常用列表
// 添加到常用商品
export function addToFavorites(data) {
return request({
url: '/jarvis/favoriteProduct/addToFavorites',
method: 'post',
data: data
data
})
}
// 从常用列表移除商品
export function removeFromFavorites(skuid) {
// 更新发品信息商品ID、状态等
export function updateProductInfo(data) {
return request({
url: '/jarvis/favoriteProduct/removeFromFavorites/' + skuid,
method: 'delete'
url: '/jarvis/favoriteProduct/updateProductInfo',
method: 'put',
data
})
}
// 更新置顶状态
export function updateTopStatus(id, isTop) {
// 从常用商品快速发品
export function quickPublish(id, appid) {
return request({
url: '/jarvis/favoriteProduct/updateTopStatus/' + id + '/' + isTop,
method: 'put'
})
}
// 根据SKUID查询常用商品
export function getBySkuid(skuid) {
return request({
url: '/jarvis/favoriteProduct/getBySkuid/' + skuid,
method: 'get'
})
}
// 查询用户的常用商品列表
export function getUserFavorites() {
return request({
url: '/jarvis/favoriteProduct/userFavorites',
method: 'get'
})
}
// 根据线报消息创建常用商品
export function createFromXbMessage(data) {
return request({
url: '/jarvis/favoriteProduct/createFromXbMessage',
method: 'post',
data: data
})
}
// 快速发品(从常用商品)
export function quickPublishFromFavorite(id, appid) {
return request({
url: '/jarvis/favoriteProduct/quickPublish/' + id,
url: `/jarvis/favoriteProduct/quickPublish/${id}`,
method: 'post',
data: appid
})
}
// 更新使用次数和最后使用时间
export function updateUseCount(id) {
return request({
url: '/jarvis/favoriteProduct/updateUseCount/' + id,
method: 'put'
})
}
// 更新发品信息
export function updateProductInfo(data) {
return request({
url: '/jarvis/favoriteProduct/updateProductInfo',
method: 'put',
data: data
})
}

View File

@@ -18,6 +18,15 @@ export function createProductByPromotion(data) {
})
}
// 上架商品
export function publishProduct(data) {
return request({
url: '/erp/product/publish',
method: 'post',
data
})
}
// 地区下拉
export function getProvinces() {
return request({

View File

@@ -0,0 +1,395 @@
<template>
<el-dialog title="发品" :visible.sync="internalVisible" width="1200px" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="false" append-to-body @close="handleClose">
<el-form :model="form" :rules="rules" ref="publishForm" label-width="110px">
<el-form-item label="文案版本" v-if="wenanOptions && wenanOptions.length > 0">
<el-select v-model="form.wenanIndex" placeholder="选择文案版本" @change="onWenanChange" style="width:100%">
<el-option v-for="(opt, idx) in wenanOptions" :key="idx" :label="opt.label" :value="idx" />
</el-select>
</el-form-item>
<el-form-item label="闲管家账号">
<el-select v-model="form.appid" filterable placeholder="选择ERP应用" :loading="erpAccountLoading" @change="onAppidChange">
<el-option v-for="a in erpAccountsOptions" :key="a.value" :label="a.label" :value="a.value" />
</el-select>
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input v-model="form.title" maxlength="34" show-word-limit />
</el-form-item>
<el-form-item label="文案内容" prop="content">
<el-input type="textarea" :rows="6" v-model="form.content" />
</el-form-item>
<el-form-item label="选择图片" v-if="productImages && productImages.length">
<div class="img-grid">
<div class="img-item" v-for="(img, idx) in productImages" :key="idx">
<img :src="img.url" :alt="`图片${idx+1}`" @click="handlePreviewImage(img.url)" />
<div class="img-actions">
<el-checkbox v-model="img.selected">使用</el-checkbox>
<el-button type="text" size="mini" @click="handleCopyImageUrl(img.url)">复制</el-button>
</div>
</div>
</div>
<div style="margin-top:8px;">
<el-button size="mini" @click="selectAllImages(true)">全选</el-button>
<el-button size="mini" @click="selectAllImages(false)">全不选</el-button>
<el-button size="mini" @click="invertSelection">反选</el-button>
</div>
</el-form-item>
<el-form-item label="额外图片链接">
<el-input type="textarea" :rows="3" v-model="form.extraImagesText" placeholder="每行一条图片URL" />
</el-form-item>
<el-form-item label="白底图">
<el-input v-model="form.whiteImages" placeholder="可选图片URL" />
</el-form-item>
<el-form-item label="服务项">
<el-select v-model="form.serviceSupport" multiple collapse-tags placeholder="可多选">
<el-option v-for="opt in serviceSupportOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="会员名" prop="userName">
<el-select v-model="form.userName" filterable placeholder="选择会员名" :loading="userNameLoading">
<el-option v-for="u in userNameOptions" :key="u.value" :label="u.label" :value="u.value" />
</el-select>
</el-form-item>
<el-form-item label="省/市/区" required>
<div style="display:flex; gap:8px; width:100%">
<el-select v-model.number="form.province" placeholder="选择省" style="flex:1" filterable @change="onProvinceChange">
<el-option v-for="p in regionOptions.provinces" :key="p.value" :label="p.label" :value="p.value" />
</el-select>
<el-select v-model.number="form.city" placeholder="选择市" style="flex:1" filterable :disabled="!form.province" @change="onCityChange">
<el-option v-for="c in regionOptions.cities" :key="c.value" :label="c.label" :value="c.value" />
</el-select>
<el-select v-model.number="form.district" placeholder="选择区" style="flex:1" filterable :disabled="!form.city">
<el-option v-for="a in regionOptions.areas" :key="a.value" :label="a.label" :value="a.value" />
</el-select>
</div>
</el-form-item>
<el-form-item label="价格(元)" prop="price">
<el-input v-model.number="form.price" type="number" min="0.01" step="0.01" />
</el-form-item>
<el-form-item label="原价(元)">
<el-input v-model.number="form.originalPrice" type="number" min="0" step="0.01" />
</el-form-item>
<el-form-item label="运费(元)" prop="expressFee">
<el-input v-model.number="form.expressFee" type="number" min="0" step="0.01" />
</el-form-item>
<el-form-item label="库存" prop="stock">
<el-input v-model.number="form.stock" type="number" min="1" />
</el-form-item>
<el-form-item label="商家编码">
<el-input v-model="form.outerId" maxlength="64" />
</el-form-item>
<el-form-item label="商品类型" prop="itemBizType">
<el-select v-model.number="form.itemBizType" filterable @change="onItemBizTypeChange">
<el-option v-for="opt in itemBizTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="行业类型" prop="spBizType">
<el-select v-model.number="form.spBizType" filterable @change="onSpBizTypeChange">
<el-option v-for="opt in spBizTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="类目ID" prop="channelCatId">
<el-select v-model="form.channelCatId" filterable placeholder="请选择类目" :disabled="!categoryOptions.length" :loading="categoryLoading" @change="loadProperties">
<el-option v-for="c in categoryOptions" :key="c.value" :label="c.label" :value="c.value" />
</el-select>
</el-form-item>
<el-form-item label="商品属性">
<div v-if="pvOptions.length" style="display:flex; flex-direction:column; gap:8px;">
<div v-for="(p, pi) in pvOptions" :key="p.propertyId" style="display:flex; gap:8px; align-items:center;">
<span style="width:90px; text-align:right; color:#666;">{{ p.propertyName }}:</span>
<el-select v-model="selectedPv[p.propertyId]" clearable filterable placeholder="请选择" style="flex:1" @change="onPvChange">
<el-option v-for="v in p.values" :key="v.valueId" :label="v.valueName" :value="v.valueId" />
</el-select>
</div>
</div>
<div v-else style="color:#999;">无属性或请选择类型和类目后加载</div>
</el-form-item>
<el-form-item label="成色">
<el-select v-model.number="form.stuffStatus" clearable filterable placeholder="可选">
<el-option v-for="opt in stuffStatusOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="属性JSON">
<el-input type="textarea" :rows="3" v-model="form.channelPvJson" placeholder='示例: [{"property_id":"p","property_name":"颜色","value_id":"v","value_name":"红"}]' />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="closeDialog"> </el-button>
<el-button type="primary" :loading="loading" @click="submitPublish"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { createProductByPromotion, publishProduct, getProvinces, getCities, getAreas, getCategories, getUsernames, getERPAccounts, getProperties } from "@/api/system/jdorder";
export default {
name: 'PublishDialog',
props: {
visible: { type: Boolean, default: false },
initialData: { type: Object, default: () => ({}) }
},
data() {
return {
internalVisible: false,
loading: false,
wenanOptions: [],
productImages: [],
form: {
appid: '',
userName: '',
province: 440000,
city: 440400,
district: 440402,
title: '',
content: '',
wenanIndex: 0,
extraImagesText: '',
whiteImages: '',
serviceSupport: ['NFR'],
price: null,
originalPrice: null,
expressFee: 0,
stock: 999,
outerId: '',
itemBizType: 2,
spBizType: 3,
channelCatId: '',
stuffStatus: 100,
channelPvJson: ''
},
rules: {
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
content: [{ required: true, message: '请输入文案内容', trigger: 'blur' }],
userName: [{ required: true, message: '请输入闲鱼会员名', trigger: 'blur' }],
price: [{ required: true, message: '请输入价格(分)', trigger: 'blur' }],
expressFee: [{ required: true, message: '请输入运费(分)', trigger: 'blur' }],
stock: [{ required: true, message: '请输入库存', trigger: 'blur' }],
itemBizType: [{ required: true, message: '请选择商品类型', trigger: 'change' }],
spBizType: [{ required: true, message: '请选择行业类型', trigger: 'change' }],
channelCatId: [{ required: true, message: '请输入类目ID', trigger: 'blur' }]
},
regionOptions: { provinces: [], cities: [], areas: [] },
categoryOptions: [],
categoryLoading: false,
userNameOptions: [],
userNameLoading: false,
erpAccountsOptions: [],
erpAccountLoading: false,
pvOptions: [],
selectedPv: {},
itemBizTypeOptions: [
{ label: '普通商品', value: 2 },
{ label: '已验货', value: 0 },
{ label: '验货宝', value: 10 },
{ label: '闲鱼优品', value: 19 },
{ label: '闲鱼特卖', value: 24 },
{ label: '品牌捡漏', value: 26 }
],
spBizTypeOptions: [
{ label: '手机', value: 1 },
{ label: '时尚', value: 2 },
{ label: '家电', value: 3 },
{ label: '乐器', value: 8 },
{ label: '数码3C', value: 9 },
{ label: '奢品', value: 16 },
{ label: '母婴', value: 17 },
{ label: '美妆', value: 18 },
{ label: '珠宝', value: 19 },
{ label: '游戏', value: 20 },
{ label: '家居', value: 21 },
{ label: '虚拟', value: 22 },
{ label: '图书', value: 24 },
{ label: '食品', value: 27 },
{ label: '玩具', value: 28 },
{ label: '其他', value: 99 }
],
stuffStatusOptions: [
{ label: '不传', value: null },
{ label: '全新', value: 100 },
{ label: '99新', value: 99 },
{ label: '95新', value: 95 },
{ label: '9成新', value: 90 },
{ label: '8成新', value: 80 },
{ label: '7成新', value: 70 },
{ label: '6成新', value: 60 },
{ label: '5成新', value: 50 }
],
serviceSupportOptions: [
{ label: '七天无理由退货', value: 'SDR' },
{ label: '描述不符包邮退', value: 'NFR' },
{ label: '描述不符全额退(虚拟)', value: 'VNR' },
{ label: '10分钟极速发货(虚拟)', value: 'FD_10MS' },
{ label: '24小时极速发货', value: 'FD_24HS' },
{ label: '48小时极速发货', value: 'FD_48HS' },
{ label: '正品保障', value: 'FD_GPA' }
]
}
},
watch: {
visible: {
immediate: true,
handler(v) { this.internalVisible = v; if (v) { this.bootstrap(); } }
},
'form.itemBizType'(val) { this.onItemBizTypeChange(); },
'form.spBizType'(val) { this.onSpBizTypeChange(); },
'form.appid'(val) { this.onAppidChange(); },
'form.channelCatId'(val) { this.loadProperties(); }
},
methods: {
async bootstrap() {
// 初始化表单与文案/图片
const d = this.initialData || {};
this.wenanOptions = Array.isArray(d.wenanOptions) ? d.wenanOptions : [];
this.form.title = d.title || '';
this.form.content = d.content || '';
this.form.wenanIndex = 0;
if (Array.isArray(d.images)) {
this.productImages = d.images.map(u => ({ url: u, selected: true }));
} else { this.productImages = []; }
// 预估原价
if (typeof d.originalPrice === 'number') {
this.form.originalPrice = d.originalPrice;
}
await this.loadProvinces();
await this.loadERPAccounts();
await this.loadUsernames();
await this.loadCategories();
this.$nextTick(() => this.$refs.publishForm && this.$refs.publishForm.clearValidate());
},
onWenanChange(val) {
if (this.wenanOptions && this.wenanOptions[val]) {
this.form.content = this.wenanOptions[val].content || '';
}
},
selectAllImages(flag) { (this.productImages || []).forEach(it => { it.selected = !!flag; }); },
invertSelection() { (this.productImages || []).forEach(it => { it.selected = !it.selected; }); },
handleCopyImageUrl(imageUrl) { if (navigator.clipboard) { navigator.clipboard.writeText(imageUrl).then(() => { this.$modal.msgSuccess('图片链接复制成功'); }).catch(() => { this.$message.error('复制失败'); }); } },
handlePreviewImage(imageUrl) { window.open(imageUrl, '_blank'); },
async loadProvinces(echo = true) {
try { const res = await getProvinces(); if (res.code === 200) this.regionOptions.provinces = res.data || []; else this.$modal.msgError(res.msg || '加载省份失败'); } catch(e){ this.$modal.msgError('加载省份失败'); }
if (echo && this.form.province) { await this.loadCities(this.form.province, true); } else { this.regionOptions.cities = []; this.regionOptions.areas = []; this.form.city = null; this.form.district = null; }
},
async onProvinceChange() { await this.loadCities(this.form.province, false); },
async onCityChange() { await this.loadAreas(this.form.province, this.form.city, false); },
async loadCities(provId, echo = false) {
if (!provId) { this.regionOptions.cities = []; this.regionOptions.areas = []; this.form.city = null; this.form.district = null; return; }
try { const res = await getCities(provId); if (res.code === 200) this.regionOptions.cities = res.data || []; else this.$modal.msgError(res.msg || '加载城市失败'); } catch(e){ this.$modal.msgError('加载城市失败'); }
if (echo && this.form.city) { await this.loadAreas(provId, this.form.city, true); } else { this.regionOptions.areas = []; this.form.district = null; }
},
async loadAreas(provId, cityId, echo = false) {
if (!provId || !cityId) { this.regionOptions.areas = []; this.form.district = null; return; }
try { const res = await getAreas(provId, cityId); if (res.code === 200) this.regionOptions.areas = res.data || []; else this.$modal.msgError(res.msg || '加载区县失败'); } catch(e){ this.$modal.msgError('加载区县失败'); }
if (!echo) { this.form.district = null; }
},
async loadERPAccounts() {
this.erpAccountLoading = true;
try { const res = await getERPAccounts(); if (res.code === 200) this.erpAccountsOptions = res.data || []; else this.$modal.msgError(res.msg || '加载应用失败'); } catch(e){ this.$modal.msgError('加载应用失败'); }
this.erpAccountLoading = false; if (!this.form.appid && this.erpAccountsOptions.length) { this.form.appid = this.erpAccountsOptions[0].value; }
},
onAppidChange() { this.form.userName = ''; this.loadUsernames(); this.loadCategories(); this.loadProperties(); },
async loadUsernames() {
this.userNameLoading = true;
try { const res = await getUsernames({ pageNum: 1, pageSize: 200, appid: this.form.appid }); if (res.code === 200) this.userNameOptions = res.data || []; else this.$modal.msgError(res.msg || '加载会员名失败'); } catch(e){ this.$modal.msgError('加载会员名失败'); }
this.userNameLoading = false; if (!this.form.userName && this.userNameOptions.length) { this.form.userName = this.userNameOptions[0].value; }
},
async onItemBizTypeChange() { this.categoryOptions = []; this.form.channelCatId = ''; await this.loadCategories(); },
async onSpBizTypeChange() { this.categoryOptions = []; this.form.channelCatId = ''; await this.loadCategories(); },
async loadCategories() {
const itemBizType = this.form.itemBizType; const spBizType = this.form.spBizType; const appid = this.form.appid; if (!itemBizType) return;
this.categoryLoading = true;
try { const res = await getCategories({ itemBizType, spBizType, appid }); if (res.code === 200) this.categoryOptions = res.data || []; else this.$modal.msgError(res.msg || '加载类目失败'); } catch(e){ this.$modal.msgError('加载类目失败'); }
this.categoryLoading = false;
if (this.form.channelCatId) { this.loadProperties(); } else if (this.categoryOptions.length) { this.form.channelCatId = this.categoryOptions[0].value; this.loadProperties(); }
},
async loadProperties() {
const f = this.form; if (!f.itemBizType || !f.spBizType || !f.channelCatId) { this.pvOptions = []; this.selectedPv = {}; return; }
try {
const res = await getProperties({ itemBizType: f.itemBizType, spBizType: f.spBizType, channelCatId: f.channelCatId, appid: f.appid });
if (res.code === 200) { this.pvOptions = res.data || []; const keep = { ...this.selectedPv }; this.selectedPv = {}; (this.pvOptions || []).forEach(p => { if (keep[p.propertyId]) this.selectedPv[p.propertyId] = keep[p.propertyId]; }); }
else { this.$modal.msgError(res.msg || '加载属性失败'); }
} catch(e) { this.$modal.msgError('加载属性失败'); }
},
submitPublish() {
this.$refs.publishForm.validate(valid => {
if (!valid) return;
const f = this.form;
const selectedImages = (this.productImages || []).filter(it => it.selected).map(it => it.url).filter(Boolean);
const extraImages = String(f.extraImagesText || '').split(/\n+/).map(s => s.trim()).filter(Boolean);
const images = [...selectedImages, ...extraImages];
if (!images.length) { this.$modal.msgError('请至少选择或填写一张图片'); return; }
let channelPv = undefined; if (f.channelPvJson && f.channelPvJson.trim()) { try { channelPv = JSON.parse(f.channelPvJson); } catch(e) { this.$modal.msgError('属性JSON格式不正确'); return; } }
const payload = {
appid: f.appid || undefined,
title: f.title,
content: f.content,
images: images,
whiteImages: f.whiteImages || undefined,
userName: f.userName,
province: f.province,
city: f.city,
district: f.district,
serviceSupport: (f.serviceSupport && f.serviceSupport.length) ? f.serviceSupport.join(',') : undefined,
price: cents(f.price),
originalPrice: f.originalPrice != null ? cents(f.originalPrice) : undefined,
expressFee: cents(f.expressFee),
stock: f.stock,
outerId: f.outerId || undefined,
itemBizType: f.itemBizType,
spBizType: f.spBizType,
channelCatId: f.channelCatId,
stuffStatus: f.stuffStatus || undefined,
channelPv: channelPv
};
function cents(yuan){ const n = Number(yuan); if (Number.isNaN(n)) return undefined; return Math.round(n*100); }
this.loading = true;
createProductByPromotion(payload).then(async res => {
this.loading = false;
if (res.code === 200) {
try {
const outerId = res.data && (res.data.outerId || (res.data.data && res.data.data.outerId));
if (outerId) this.$modal.msgSuccess(`发品成功,商家编码:${outerId}`); else this.$modal.msgSuccess('发品提交成功');
} catch(e){ this.$modal.msgSuccess('发品提交成功'); }
// 自动上架(需要返回的 productId 与 userName
try {
const productId = this.extractProductId(res.data);
if (productId && this.form.userName) {
const pubRes = await publishProduct({ productId, userName: this.form.userName, appid: this.form.appid });
if (pubRes && pubRes.code === 200) {
const code = (pubRes.data && pubRes.data.code) ?? pubRes.code;
if (code === 0 || code === 200) this.$modal.msgSuccess('上架成功'); else this.$modal.msgWarning('上架已提交或状态未知');
} else {
this.$modal.msgError((pubRes && pubRes.msg) || '上架失败');
}
}
} catch (e) { /* 忽略上架异常,仅提示 */ }
this.$emit('success', res);
this.closeDialog();
} else { this.$modal.msgError(res.msg || '发品失败'); }
}).catch(err => { this.loading = false; console.error('发品失败', err); this.$modal.msgError('发品失败,请稍后重试'); });
});
},
extractProductId(resp) {
try {
if (!resp) return null;
if (typeof resp === 'object') {
if (resp.productId) return Number(resp.productId);
if (resp.data && resp.data.productId) return Number(resp.data.productId);
if (resp.data && resp.data.id) return Number(resp.data.id);
}
} catch (e) { return null; }
return null;
},
closeDialog() { this.$emit('update:visible', false); this.internalVisible = false; },
handleClose() { this.closeDialog(); }
}
}
</script>
<style scoped>
.img-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; }
.img-item { border: 1px solid #e5e5e5; border-radius: 4px; padding: 6px; text-align: center; }
.img-item img { width: 100%; height: 100px; object-fit: cover; border-radius: 4px; }
.img-actions { display: flex; justify-content: space-between; align-items: center; margin-top: 6px; }
</style>

View File

@@ -98,8 +98,9 @@
</div>
<!-- 发品操作 -->
<div style="margin-top: 10px;">
<div style="margin-top: 10px; display:flex; gap:8px;">
<el-button type="primary" @click="openPublish(product, productIndex)">发品</el-button>
<el-button @click="handleAddToFavorites(product)">加入常用</el-button>
</div>
</el-tab-pane>
</el-tabs>
@@ -122,133 +123,19 @@
</div>
</el-card>
<!-- 发品对话框 -->
<el-dialog title="发品" :visible.sync="publishDialog.visible" width="1200px" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="false" append-to-body>
<el-form :model="publishDialog.form" :rules="publishDialog.rules" ref="publishForm" label-width="110px">
<el-form-item label="文案版本" v-if="publishDialog.wenanOptions && publishDialog.wenanOptions.length > 0">
<el-select v-model="publishDialog.form.wenanIndex" placeholder="选择文案版本" @change="onWenanChange" style="width:100%">
<el-option v-for="(opt, idx) in publishDialog.wenanOptions" :key="idx" :label="opt.label" :value="idx" />
</el-select>
</el-form-item>
<el-form-item label="闲管家账号">
<el-select v-model="publishDialog.form.appid" filterable placeholder="选择ERP应用" :loading="erpAccountLoading" @change="onAppidChange">
<el-option v-for="a in erpAccountsOptions" :key="a.value" :label="a.label" :value="a.value" />
</el-select>
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input v-model="publishDialog.form.title" maxlength="60" show-word-limit />
</el-form-item>
<el-form-item label="文案内容" prop="content">
<el-input type="textarea" :rows="6" v-model="publishDialog.form.content" />
</el-form-item>
<el-form-item label="选择图片" v-if="publishDialog.productImages && publishDialog.productImages.length">
<div class="img-grid">
<div class="img-item" v-for="(img, idx) in publishDialog.productImages" :key="idx">
<img :src="img.url" :alt="`图片${idx+1}`" @click="handlePreviewImage(img.url)" />
<div class="img-actions">
<el-checkbox v-model="img.selected">使用</el-checkbox>
<el-button type="text" size="mini" @click="handleCopyImageUrl(img.url)">复制</el-button>
</div>
</div>
</div>
<div style="margin-top:8px;">
<el-button size="mini" @click="selectAllImages(true)">全选</el-button>
<el-button size="mini" @click="selectAllImages(false)">全不选</el-button>
<el-button size="mini" @click="invertSelection">反选</el-button>
</div>
</el-form-item>
<el-form-item label="额外图片链接">
<el-input type="textarea" :rows="3" v-model="publishDialog.form.extraImagesText" placeholder="每行一条图片URL" />
</el-form-item>
<el-form-item label="白底图">
<el-input v-model="publishDialog.form.whiteImages" placeholder="可选图片URL" />
</el-form-item>
<el-form-item label="服务项">
<el-select v-model="publishDialog.form.serviceSupport" multiple collapse-tags placeholder="可多选">
<el-option v-for="opt in serviceSupportOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="会员名" prop="userName">
<el-select v-model="publishDialog.form.userName" filterable placeholder="选择会员名" :loading="userNameLoading">
<el-option v-for="u in userNameOptions" :key="u.value" :label="u.label" :value="u.value" />
</el-select>
</el-form-item>
<el-form-item label="省/市/区" required>
<div style="display:flex; gap:8px; width:100%">
<el-select v-model.number="publishDialog.form.province" placeholder="选择省" style="flex:1" filterable @change="onProvinceChange">
<el-option v-for="p in regionOptions.provinces" :key="p.value" :label="p.label" :value="p.value" />
</el-select>
<el-select v-model.number="publishDialog.form.city" placeholder="选择市" style="flex:1" filterable :disabled="!publishDialog.form.province" @change="onCityChange">
<el-option v-for="c in regionOptions.cities" :key="c.value" :label="c.label" :value="c.value" />
</el-select>
<el-select v-model.number="publishDialog.form.district" placeholder="选择区" style="flex:1" filterable :disabled="!publishDialog.form.city">
<el-option v-for="a in regionOptions.areas" :key="a.value" :label="a.label" :value="a.value" />
</el-select>
</div>
</el-form-item>
<el-form-item label="价格(元)" prop="price">
<el-input v-model.number="publishDialog.form.price" type="number" min="0.01" step="0.01" />
</el-form-item>
<el-form-item label="原价(元)">
<el-input v-model.number="publishDialog.form.originalPrice" type="number" min="0" step="0.01" />
</el-form-item>
<el-form-item label="运费(元)" prop="expressFee">
<el-input v-model.number="publishDialog.form.expressFee" type="number" min="0" step="0.01" />
</el-form-item>
<el-form-item label="库存" prop="stock">
<el-input v-model.number="publishDialog.form.stock" type="number" min="1" />
</el-form-item>
<el-form-item label="商家编码">
<el-input v-model="publishDialog.form.outerId" maxlength="64" />
</el-form-item>
<el-form-item label="商品类型" prop="itemBizType">
<el-select v-model.number="publishDialog.form.itemBizType" filterable @change="onItemBizTypeChange">
<el-option v-for="opt in itemBizTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="行业类型" prop="spBizType">
<el-select v-model.number="publishDialog.form.spBizType" filterable @change="onSpBizTypeChange">
<el-option v-for="opt in spBizTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="类目ID" prop="channelCatId">
<el-select v-model="publishDialog.form.channelCatId" filterable placeholder="请选择类目" :disabled="!categoryOptions.length" :loading="categoryLoading" @change="loadProperties">
<el-option v-for="c in categoryOptions" :key="c.value" :label="c.label" :value="c.value" />
</el-select>
</el-form-item>
<el-form-item label="商品属性">
<div v-if="pvOptions.length" style="display:flex; flex-direction:column; gap:8px;">
<div v-for="(p, pi) in pvOptions" :key="p.propertyId" style="display:flex; gap:8px; align-items:center;">
<span style="width:90px; text-align:right; color:#666;">{{ p.propertyName }}:</span>
<el-select v-model="selectedPv[p.propertyId]" clearable filterable placeholder="请选择" style="flex:1" @change="onPvChange">
<el-option v-for="v in p.values" :key="v.valueId" :label="v.valueName" :value="v.valueId" />
</el-select>
</div>
</div>
<div v-else style="color:#999;">无属性或请选择类型和类目后加载</div>
</el-form-item>
<el-form-item label="成色">
<el-select v-model.number="publishDialog.form.stuffStatus" clearable filterable placeholder="可选">
<el-option v-for="opt in stuffStatusOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="属性JSON">
<el-input type="textarea" :rows="3" v-model="publishDialog.form.channelPvJson" placeholder='示例: [{"property_id":"p","property_name":"颜色","value_id":"v","value_name":"红"}]' />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="publishDialog.visible=false"> </el-button>
<el-button type="primary" :loading="publishDialog.loading" @click="submitPublish"> </el-button>
</div>
</el-dialog>
<!-- 公共发品对话框组件 -->
<PublishDialog :visible.sync="publishDialogVisible" :initial-data="publishInitialData" @success="handlePublishSuccess" />
</div>
</template>
<script>
import { generatePromotionContent, createProductByPromotion, getProvinces, getCities, getAreas, getCategories, getUsernames, getERPAccounts, getProperties } from "@/api/system/jdorder";
import { generatePromotionContent } from "@/api/system/jdorder";
import { addToFavorites } from "@/api/system/favoriteProduct";
import PublishDialog from '@/components/PublishDialog.vue'
export default {
name: "Jdorder",
components: { PublishDialog },
data() {
return {
form: {
@@ -261,46 +148,8 @@ export default {
activeProductTab: 0,
// 每个商品的文案标签页状态
activeWenanTab: {},
publishDialog: {
visible: false,
loading: false,
wenanOptions: [],
productImages: [],
form: {
appid: '',
userName: "",
province: 440000,
city: 440400,
district: 440402,
title: "",
content: "",
wenanIndex: 0,
extraImagesText: "",
whiteImages: "",
serviceSupport: ['NFR'],
price: null,
originalPrice: null,
expressFee: 0,
stock: 999,
outerId: "",
itemBizType: 2,
spBizType: 3,
channelCatId: "",
stuffStatus: 100,
channelPvJson: ""
},
rules: {
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
content: [{ required: true, message: '请输入文案内容', trigger: 'blur' }],
userName: [{ required: true, message: '请输入闲鱼会员名', trigger: 'blur' }],
price: [{ required: true, message: '请输入价格(分)', trigger: 'blur' }],
expressFee: [{ required: true, message: '请输入运费(分)', trigger: 'blur' }],
stock: [{ required: true, message: '请输入库存', trigger: 'blur' }],
itemBizType: [{ required: true, message: '请选择商品类型', trigger: 'change' }],
spBizType: [{ required: true, message: '请选择行业类型', trigger: 'change' }],
channelCatId: [{ required: true, message: '请输入类目ID', trigger: 'blur' }]
}
},
publishDialogVisible: false,
publishInitialData: {},
regionOptions: {
provinces: [],
cities: [],
@@ -362,13 +211,7 @@ export default {
]
};
},
mounted() {
// 页面加载即预拉省份与类目下拉,避免弹窗时再等待
this.loadProvinces();
this.loadERPAccounts();
this.loadCategories();
this.loadUsernames();
},
mounted() {},
watch: {
'publishDialog.form.itemBizType'(val) {
// 冗余联动,防止@change未触发的情况
@@ -386,6 +229,28 @@ export default {
}
},
methods: {
async handleAddToFavorites(product) {
try {
const payload = {
skuid: product.skuid || product.skuId || product.spuid || '',
productName: product.skuName || product.title || '',
shopName: product.shopName || '',
productUrl: product.url || '',
productImage: Array.isArray(product.images) && product.images.length ? product.images[0] : '',
price: product.price != null ? String(product.price) : (product.lowestCouponPrice != null ? String(product.lowestCouponPrice) : ''),
commissionInfo: product.commission != null ? String(product.commission) : '',
remark: '来自一键转链'
}
const res = await addToFavorites(payload)
if (res && (res.code === 200 || res.msg === '操作成功')) {
this.$modal.msgSuccess('已加入常用');
} else {
this.$modal.msgError(res && res.msg ? res.msg : '加入常用失败');
}
} catch (e) {
this.$modal.msgError('加入常用失败');
}
},
handleGenerate() {
if (!this.form.inputContent.trim()) {
this.$modal.msgError("请输入需要转链的内容");
@@ -512,43 +377,17 @@ export default {
openPublish(product, productIndex) {
const wenanIndex = this.activeWenanTab[productIndex] || 0;
const wenanContent = product && product.wenan && product.wenan[wenanIndex] ? (product.wenan[wenanIndex].content || '') : '';
const images = Array.isArray(product.images) ? product.images : [];
const wenanOptions = Array.isArray(product.wenan) ? product.wenan.map((w, i) => ({ label: w.type || `版本${i+1}`, content: w.content || '' })) : [];
this.publishDialog.wenanOptions = wenanOptions;
this.publishDialog.form = Object.assign({}, this.publishDialog.form, {
this.publishInitialData = {
title: product.skuName || '',
content: wenanContent,
wenanIndex: wenanIndex,
extraImagesText: '',
whiteImages: '',
serviceSupport: this.publishDialog.form.serviceSupport,
userName: '',
province: this.publishDialog.form.province,
city: this.publishDialog.form.city,
district: this.publishDialog.form.district,
price: null,
originalPrice: null,
expressFee: 0,
stock: this.publishDialog.form.stock,
outerId: '',
itemBizType: this.publishDialog.form.itemBizType,
spBizType: this.publishDialog.form.spBizType,
channelCatId: '',
stuffStatus: this.publishDialog.form.stuffStatus,
channelPvJson: ''
});
// 预填原价(元)
const guessed = this.guessYuanPrice(product);
if (guessed != null) {
this.publishDialog.form.originalPrice = Number(Number(guessed).toFixed(2));
}
this.publishDialog.productImages = images.map(u => ({ url: u, selected: true }));
this.publishDialog.visible = true;
this.$nextTick(() => this.$refs.publishForm && this.$refs.publishForm.clearValidate());
// 载入省份
this.loadProvinces();
content: (product && product.wenan && product.wenan[wenanIndex] ? (product.wenan[wenanIndex].content || '') : ''),
images: Array.isArray(product.images) ? product.images : [],
originalPrice: this.guessYuanPrice(product),
wenanOptions
};
this.publishDialogVisible = true;
},
handlePublishSuccess() { /* 可按需刷新列表或做其他处理 */ },
onWenanChange(val) {
if (this.publishDialog.wenanOptions && this.publishDialog.wenanOptions[val]) {
@@ -563,7 +402,7 @@ export default {
(this.publishDialog.productImages || []).forEach(it => { it.selected = !it.selected; });
},
// 从解析到的商品信息中推测原价(元) —— 后端已保证返回“元”,不再做分到元换算
// 从解析到的商品信息中推测原价(元)
guessYuanPrice(product) {
// 常见字段尝试price、oriPrice、originPrice、jdPrice、marketPrice 等(单位可能为元)
const candidates = [
@@ -768,7 +607,17 @@ export default {
createProductByPromotion(payload).then(res => {
this.publishDialog.loading = false;
if (res.code === 200) {
this.$modal.msgSuccess('发品提交成功');
// 成功反馈包含生成的outerId
try {
const outerId = res.data && (res.data.outerId || (res.data.data && res.data.data.outerId))
if (outerId) {
this.$modal.msgSuccess(`发品成功,商家编码:${outerId}`)
} else {
this.$modal.msgSuccess('发品提交成功')
}
} catch (e) {
this.$modal.msgSuccess('发品提交成功')
}
this.publishDialog.visible = false;
} else {
this.$modal.msgError(res.msg || '发品失败');

View File

@@ -260,9 +260,10 @@
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<el-table-column label="操作" align="center" width="160">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-info" @click.stop="showChildDetail(scope.row)">详情</el-button>
<el-button size="mini" type="text" icon="el-icon-s-promotion" @click.stop="openPublishFromXb(scope.row)">发品</el-button>
</template>
</el-table-column>
</el-table>
@@ -274,269 +275,21 @@
</div>
</el-dialog>
<!-- 一键转链弹窗 -->
<el-dialog title="一键转链" :visible.sync="quickLinkVisible" width="90%" :close-on-click-modal="false" append-to-body class="quick-link-dialog">
<div v-if="quickLinkProduct">
<el-alert
title="基于线报消息商品信息生成转链内容"
type="info"
:closable="false"
style="margin-bottom: 20px;"
/>
<!-- 商品基本信息展示 -->
<el-descriptions :column="3" border style="margin-bottom: 25px;">
<el-descriptions-item label="商品名称" :span="2">{{ decodeUnicode(quickLinkProduct.skuName) || '-' }}</el-descriptions-item>
<el-descriptions-item label="价格">{{ quickLinkProduct.firstPrice ? `¥${quickLinkProduct.firstPrice}` : '-' }}</el-descriptions-item>
<el-descriptions-item label="店铺名称">{{ decodeUnicode(getJsonValue(quickLinkProduct.jsonQueryResult, 'shopInfo.shopName')) || '-' }}</el-descriptions-item>
<el-descriptions-item label="佣金">{{ getJsonValue(quickLinkProduct.jsonQueryResult, 'commissionInfo.commission') ? `¥${getJsonValue(quickLinkProduct.jsonQueryResult, 'commissionInfo.commission')}` : '-' }}</el-descriptions-item>
<el-descriptions-item label="佣金比例">{{ getJsonValue(quickLinkProduct.jsonQueryResult, 'commissionInfo.commissionShare') ? `${getJsonValue(quickLinkProduct.jsonQueryResult, 'commissionInfo.commissionShare')}%` : '-' }}</el-descriptions-item>
</el-descriptions>
<!-- 转链结果展示 -->
<div v-if="quickLinkResult" style="margin-top: 20px;">
<h4>转链结果</h4>
<!-- 商品列表 -->
<div v-if="parsedQuickLinkResult && Array.isArray(parsedQuickLinkResult) && parsedQuickLinkResult.length > 0" style="margin-bottom: 20px;">
<h5>商品列表 ({{ parsedQuickLinkResult.length }}个商品)</h5>
<el-tabs v-model="activeQuickLinkTab" type="card">
<el-tab-pane
v-for="(product, productIndex) in parsedQuickLinkResult"
:key="productIndex"
:label="`商品${productIndex + 1}`"
:name="productIndex"
>
<!-- 商品基本信息 -->
<el-descriptions :column="2" border style="margin-bottom: 20px;">
<el-descriptions-item label="商品名称">{{ product.skuName || '-' }}</el-descriptions-item>
<el-descriptions-item label="店铺">{{ product.shopName || '-' }}</el-descriptions-item>
<el-descriptions-item label="佣金">¥{{ product.commission || '-' }}</el-descriptions-item>
<el-descriptions-item label="佣金比例">{{ product.commissionShare || '-' }}%</el-descriptions-item>
<el-descriptions-item label="SPUID">{{ product.spuid || '-' }}</el-descriptions-item>
<el-descriptions-item label="商品链接">
<a :href="product.url || '#'" target="_blank" style="color: #409EFF;">{{ product.url || '-' }}</a>
</el-descriptions-item>
</el-descriptions>
<!-- 文案版本 -->
<div v-if="product.wenan && Array.isArray(product.wenan) && product.wenan.length > 0" style="margin-bottom: 20px;">
<h6>文案版本 ({{ product.wenan.length }})</h6>
<el-tabs v-model="activeQuickLinkWenanTab[productIndex]" type="border-card">
<el-tab-pane
v-for="(wenan, wenanIndex) in product.wenan"
:key="wenanIndex"
:label="wenan.type || `版本${wenanIndex + 1}`"
:name="wenanIndex"
>
<el-input
:value="wenan.content || ''"
type="textarea"
:rows="8"
readonly
style="width: 100%"
/>
<div style="margin-top: 10px;">
<el-button type="success" @click="handleCopyText(wenan.content || '')">复制此版本</el-button>
<el-button type="primary" style="margin-left: 8px;" @click="openQuickLinkPublish(product, productIndex)">发品</el-button>
</div>
</el-tab-pane>
</el-tabs>
</div>
<!-- 商品图片 -->
<div v-if="product.images && Array.isArray(product.images) && product.images.length > 0" style="margin-bottom: 20px;">
<h6>商品图片 ({{ product.images.length }})</h6>
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
<div
v-for="(image, imageIndex) in product.images"
:key="imageIndex"
style="text-align: center;"
>
<img
:src="image"
:alt="`商品${productIndex + 1}图片${imageIndex + 1}`"
style="width: 120px; height: 120px; object-fit: cover; border: 1px solid #ddd; border-radius: 4px;"
@click="handlePreviewImage(image)"
/>
<div style="margin-top: 5px; font-size: 12px; color: #666;">
<el-button type="text" size="mini" @click="handleCopyImageUrl(image)">复制链接</el-button>
</div>
</div>
</div>
</div>
<!-- 发品操作 -->
<div style="margin-top: 10px;">
<el-button type="primary" @click="openQuickLinkPublish(product, productIndex)">发品</el-button>
</div>
</el-tab-pane>
</el-tabs>
</div>
<!-- 原始数据 -->
<div style="margin-top: 20px;">
<h5>原始数据</h5>
<el-input
:value="quickLinkResult"
type="textarea"
:rows="6"
readonly
style="width: 100%"
/>
<div style="margin-top: 10px;">
<el-button type="success" @click="handleCopyQuickLinkResult">复制原始数据</el-button>
</div>
</div>
</div>
<!-- 转链操作 -->
<div v-if="!quickLinkResult" style="margin-top: 20px; text-align: center;">
<el-button type="primary" @click="generateQuickLink" :loading="quickLinkLoading" size="large">
生成转链内容
</el-button>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="quickLinkVisible = false"> </el-button>
</div>
</el-dialog>
<!-- 发品对话框 -->
<el-dialog title="发品" :visible.sync="quickLinkPublishDialog.visible" width="90%" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="false" append-to-body>
<el-form :model="quickLinkPublishDialog.form" :rules="quickLinkPublishDialog.rules" ref="quickLinkPublishForm" label-width="120px" size="medium">
<el-form-item label="文案版本" v-if="quickLinkPublishDialog.wenanOptions && quickLinkPublishDialog.wenanOptions.length > 0">
<el-select v-model="quickLinkPublishDialog.form.wenanIndex" placeholder="选择文案版本" @change="onQuickLinkWenanChange" style="width:100%">
<el-option v-for="(opt, idx) in quickLinkPublishDialog.wenanOptions" :key="idx" :label="opt.label" :value="idx" />
</el-select>
</el-form-item>
<el-form-item label="闲管家账号">
<el-select v-model="quickLinkPublishDialog.form.appid" filterable placeholder="选择ERP应用" :loading="erpAccountLoading" @change="onQuickLinkAppidChange">
<el-option v-for="a in erpAccountsOptions" :key="a.value" :label="a.label" :value="a.value" />
</el-select>
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input v-model="quickLinkPublishDialog.form.title" maxlength="60" show-word-limit />
</el-form-item>
<el-form-item label="文案内容" prop="content">
<el-input type="textarea" :rows="6" v-model="quickLinkPublishDialog.form.content" />
</el-form-item>
<el-form-item label="选择图片" v-if="quickLinkPublishDialog.productImages && quickLinkPublishDialog.productImages.length">
<div class="img-grid">
<div class="img-item" v-for="(img, idx) in quickLinkPublishDialog.productImages" :key="idx">
<img :src="img.url" :alt="`图片${idx+1}`" @click="handlePreviewImage(img.url)" />
<div class="img-actions">
<el-checkbox v-model="img.selected">使用</el-checkbox>
<el-button type="text" size="mini" @click="handleCopyImageUrl(img.url)">复制</el-button>
</div>
</div>
</div>
<div style="margin-top:8px;">
<el-button size="mini" @click="selectAllQuickLinkImages(true)">全选</el-button>
<el-button size="mini" @click="selectAllQuickLinkImages(false)">全不选</el-button>
<el-button size="mini" @click="invertQuickLinkSelection">反选</el-button>
</div>
</el-form-item>
<el-form-item label="额外图片链接">
<el-input type="textarea" :rows="3" v-model="quickLinkPublishDialog.form.extraImagesText" placeholder="每行一条图片URL" />
</el-form-item>
<el-form-item label="白底图">
<el-input v-model="quickLinkPublishDialog.form.whiteImages" placeholder="可选图片URL" />
</el-form-item>
<el-form-item label="服务项">
<el-select v-model="quickLinkPublishDialog.form.serviceSupport" multiple collapse-tags placeholder="可多选">
<el-option v-for="opt in serviceSupportOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="会员名" prop="userName">
<el-select v-model="quickLinkPublishDialog.form.userName" filterable placeholder="选择会员名" :loading="userNameLoading">
<el-option v-for="u in userNameOptions" :key="u.value" :label="u.label" :value="u.value" />
</el-select>
</el-form-item>
<el-form-item label="省/市/区" required>
<div style="display:flex; gap:8px; width:100%">
<el-select v-model.number="quickLinkPublishDialog.form.province" placeholder="选择省" style="flex:1" filterable @change="onQuickLinkProvinceChange">
<el-option v-for="p in regionOptions.provinces" :key="p.value" :label="p.label" :value="p.value" />
</el-select>
<el-select v-model.number="quickLinkPublishDialog.form.city" placeholder="选择市" style="flex:1" filterable :disabled="!quickLinkPublishDialog.form.province" @change="onQuickLinkCityChange">
<el-option v-for="c in regionOptions.cities" :key="c.value" :label="c.label" :value="c.value" />
</el-select>
<el-select v-model.number="quickLinkPublishDialog.form.district" placeholder="选择区" style="flex:1" filterable :disabled="!quickLinkPublishDialog.form.city">
<el-option v-for="a in regionOptions.areas" :key="a.value" :label="a.label" :value="a.value" />
</el-select>
</div>
</el-form-item>
<el-form-item label="价格(元)" prop="price">
<el-input v-model.number="quickLinkPublishDialog.form.price" type="number" min="0.01" step="0.01" />
</el-form-item>
<el-form-item label="原价(元)">
<el-input v-model.number="quickLinkPublishDialog.form.originalPrice" type="number" min="0" step="0.01" />
</el-form-item>
<el-form-item label="运费(元)" prop="expressFee">
<el-input v-model.number="quickLinkPublishDialog.form.expressFee" type="number" min="0" step="0.01" />
</el-form-item>
<el-form-item label="库存" prop="stock">
<el-input v-model.number="quickLinkPublishDialog.form.stock" type="number" min="1" />
</el-form-item>
<el-form-item label="商家编码">
<el-input v-model="quickLinkPublishDialog.form.outerId" maxlength="64" placeholder="留空则自动生成SKUID+001/002/003..." />
<div style="font-size: 12px; color: #909399; margin-top: 4px;">
格式SKUID + 001/002/003... 递增编号留空则系统自动生成
</div>
</el-form-item>
<el-form-item label="商品类型" prop="itemBizType">
<el-select v-model.number="quickLinkPublishDialog.form.itemBizType" filterable @change="onQuickLinkItemBizTypeChange">
<el-option v-for="opt in itemBizTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="行业类型" prop="spBizType">
<el-select v-model.number="quickLinkPublishDialog.form.spBizType" filterable @change="onQuickLinkSpBizTypeChange">
<el-option v-for="opt in spBizTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="类目ID" prop="channelCatId">
<el-select v-model="quickLinkPublishDialog.form.channelCatId" filterable placeholder="请选择类目" :disabled="!categoryOptions.length" :loading="categoryLoading" @change="loadQuickLinkProperties">
<el-option v-for="c in categoryOptions" :key="c.value" :label="c.label" :value="c.value" />
</el-select>
</el-form-item>
<el-form-item label="商品属性">
<div v-if="pvOptions.length" style="display:flex; flex-direction:column; gap:8px;">
<div v-for="(p, pi) in pvOptions" :key="p.propertyId" style="display:flex; gap:8px; align-items:center;">
<span style="width:90px; text-align:right; color:#666;">{{ p.propertyName }}:</span>
<el-select v-model="selectedPv[p.propertyId]" clearable filterable placeholder="请选择" style="flex:1" @change="onQuickLinkPvChange">
<el-option v-for="v in p.values" :key="v.valueId" :label="v.valueName" :value="v.valueId" />
</el-select>
</div>
</div>
<div v-else style="color:#999;">无属性或请选择类型和类目后加载</div>
</el-form-item>
<el-form-item label="成色">
<el-select v-model.number="quickLinkPublishDialog.form.stuffStatus" clearable filterable placeholder="可选">
<el-option v-for="opt in stuffStatusOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="属性JSON">
<el-input type="textarea" :rows="3" v-model="quickLinkPublishDialog.form.channelPvJson" placeholder='示例: [{"property_id":"p","property_name":"颜色","value_id":"v","value_name":"红"}]' />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="quickLinkPublishDialog.visible=false"> </el-button>
<el-button type="primary" :loading="quickLinkPublishDialog.loading" @click="submitQuickLinkPublish"> </el-button>
</div>
</el-dialog>
<!-- 公共发品对话框组件复用 -->
<PublishDialog :visible.sync="publishDialogVisible" :initial-data="publishInitialData" />
</div>
</template>
<script>
import { listXbmessage, delXbmessage, getGroupNameOptions } from "@/api/system/xbmessage";
import { parseTime } from "@/utils/ruoyi";
import PublishDialog from '@/components/PublishDialog.vue'
import { generatePromotionContent, createProductByPromotion, getProvinces, getCities, getAreas, getCategories, getUsernames, getERPAccounts, getProperties } from "@/api/system/jdorder";
import { addToFavorites, getBySkuid } from "@/api/system/favoriteProduct";
export default {
name: "Xbmessage",
components: { PublishDialog },
data() {
return {
// 遮罩层
@@ -569,6 +322,8 @@ export default {
statistics: null,
// 群名称选项
groupNameOptions: [],
publishDialogVisible: false,
publishInitialData: {},
// 一键转链相关
quickLinkVisible: false,
quickLinkProduct: null,
@@ -691,22 +446,20 @@ export default {
created() {
this.getList();
this.getGroupNameOptions();
// 预加载转链相关数据
this.loadProvinces();
this.loadERPAccounts();
this.loadCategories();
this.loadUsernames();
},
created() {
this.getList();
this.getGroupNameOptions();
// 预加载转链相关数据
this.loadProvinces();
this.loadERPAccounts();
this.loadCategories();
this.loadUsernames();
},
methods: {
openPublishFromXb(child) {
const parsedQuery = this.safeJsonParse(child.jsonQueryResult);
const images = (parsedQuery && parsedQuery.images && Array.isArray(parsedQuery.images)) ? parsedQuery.images : [];
this.publishInitialData = {
title: this.decodeUnicode(child.skuName) || '',
content: '',
images: images,
originalPrice: parsedQuery && parsedQuery.priceInfo ? Number(parsedQuery.priceInfo.price || parsedQuery.priceInfo.lowestCouponPrice || 0) : null,
wenanOptions: []
};
this.publishDialogVisible = true;
},
/** 查询消息列表 */
getList() {
this.loading = true;