|
@@ -186,49 +186,47 @@
|
|
|
</el-radio-group>
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
|
|
|
- <!-- 线上-可选人(4):服务者配置(每个服务者=一个服务商,可独立定价+可预约时段) -->
|
|
|
|
|
|
|
+ <!-- 线上-可选人(4):从已入驻服务商中选择 -->
|
|
|
<el-form-item v-if="form.deliveryMethod === 4" label="线上服务者">
|
|
<el-form-item v-if="form.deliveryMethod === 4" label="线上服务者">
|
|
|
<div class="service-person-list">
|
|
<div class="service-person-list">
|
|
|
|
|
+ <!-- 添加服务者下拉 -->
|
|
|
|
|
+ <div class="add-person-row">
|
|
|
|
|
+ <el-select v-model="selectedGuideId" placeholder="选择服务商(已入驻)" style="width: 260px"
|
|
|
|
|
+ filterable @change="onSelectGuide">
|
|
|
|
|
+ <el-option v-for="g in filteredGuides" :key="g.userId"
|
|
|
|
|
+ :label="g.realName || g.nickname"
|
|
|
|
|
+ :value="g.userId">
|
|
|
|
|
+ <span style="float:left">{{ g.realName || g.nickname }}</span>
|
|
|
|
|
+ <span style="float:right;color:#999;font-size:12px">{{ g.title || g.specialty || '' }}</span>
|
|
|
|
|
+ </el-option>
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ <el-button type="primary" size="mini" style="margin-left:8px" @click="addSelectedGuide">添加</el-button>
|
|
|
|
|
+ <div class="delivery-config-hint" style="margin-top:4px">时段由服务商在「服务商管理」中统一配置</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 已选服务者列表 -->
|
|
|
<div v-for="(person, pIdx) in servicePersons" :key="pIdx" class="service-person-card">
|
|
<div v-for="(person, pIdx) in servicePersons" :key="pIdx" class="service-person-card">
|
|
|
<div class="sp-header">
|
|
<div class="sp-header">
|
|
|
- <span class="sp-title">服务者 {{ pIdx + 1 }}</span>
|
|
|
|
|
|
|
+ <span class="sp-title">{{ person.name || '服务者 ' + (pIdx+1) }}</span>
|
|
|
<el-button type="text" size="mini" @click="removeServicePerson(pIdx)">删除</el-button>
|
|
<el-button type="text" size="mini" @click="removeServicePerson(pIdx)">删除</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
<el-row :gutter="10">
|
|
<el-row :gutter="10">
|
|
|
- <el-col :span="8">
|
|
|
|
|
- <el-input v-model="person.userId" placeholder="服务商用户ID" />
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="8">
|
|
|
|
|
- <el-input v-model="person.name" placeholder="展示名称" />
|
|
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-input v-model="person.name" placeholder="展示名称" size="small" />
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <el-col :span="8">
|
|
|
|
|
- <el-input v-model="person.title" placeholder="头衔(如高级营养师)" />
|
|
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-input v-model="person.title" placeholder="头衔" size="small" />
|
|
|
</el-col>
|
|
</el-col>
|
|
|
</el-row>
|
|
</el-row>
|
|
|
- <el-row :gutter="10" style="margin-top: 8px">
|
|
|
|
|
- <el-col :span="8">
|
|
|
|
|
- <el-input v-model="person.avatar" placeholder="头像URL" />
|
|
|
|
|
|
|
+ <el-row :gutter="10" style="margin-top:8px">
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-input v-model.number="person.price" placeholder="专属价格(元)" size="small" type="number" />
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <el-col :span="8">
|
|
|
|
|
- <el-input v-model.number="person.price" placeholder="专属价格(元)" />
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="8">
|
|
|
|
|
- <el-input v-model.number="person.originalPrice" placeholder="原价(元)" />
|
|
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-input v-model.number="person.originalPrice" placeholder="原价(元)" size="small" type="number" />
|
|
|
</el-col>
|
|
</el-col>
|
|
|
</el-row>
|
|
</el-row>
|
|
|
- <el-input v-model="person.intro" type="textarea" :rows="2" placeholder="个人介绍" style="margin-top: 8px" />
|
|
|
|
|
- <div class="sp-slots">
|
|
|
|
|
- <div v-for="(slot, sIdx) in person.slots" :key="sIdx" class="sp-slot-row">
|
|
|
|
|
- <el-date-picker v-model="slot.startTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="开始时间" style="width: 190px" />
|
|
|
|
|
- <el-date-picker v-model="slot.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="结束时间" style="width: 190px; margin-left: 8px" />
|
|
|
|
|
- <el-input v-model.number="slot.maxCapacity" placeholder="容量" style="width: 90px; margin-left: 8px" />
|
|
|
|
|
- <el-button type="text" size="mini" @click="removeServicePersonSlot(pIdx, sIdx)">删除</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-button type="text" size="mini" @click="addServicePersonSlot(pIdx)">+ 添加时段</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <el-input v-model="person.intro" type="textarea" :rows="2" placeholder="个人介绍(可留空,自动从服务商资料代入)" size="small" style="margin-top:8px" />
|
|
|
</div>
|
|
</div>
|
|
|
- <el-button type="primary" plain size="mini" @click="addServicePerson">+ 添加服务者</el-button>
|
|
|
|
|
- <div class="delivery-config-hint">服务者必须已通过服务商入驻审核(userId 为系统用户ID)</div>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
|
|
@@ -313,7 +311,7 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
-import { getProduct, createProduct, updateProduct, listAllSupplySystems, getPredefinedPurchaseFields, getProductPurchaseFields, saveProductPurchaseFields, getMySupplySystem, saveProductGiftRule, listProductGiftRules, listMembershipLevels, getGrowthArchiveProduct, saveGrowthArchiveProduct, listDeliveryServicePersons, saveDeliveryServicePersons, listDeliveryOnlineSlots, saveDeliveryOnlineSlots, getProductList, getProductSkuList, saveBundleItems, getBundleItems } from '@/api/admin'
|
|
|
|
|
|
|
+import { getProduct, createProduct, updateProduct, listAllSupplySystems, getPredefinedPurchaseFields, getProductPurchaseFields, saveProductPurchaseFields, getMySupplySystem, saveProductGiftRule, listProductGiftRules, listMembershipLevels, getGrowthArchiveProduct, saveGrowthArchiveProduct, listDeliveryServicePersons, saveDeliveryServicePersons, listDeliveryOnlineSlots, saveDeliveryOnlineSlots, getProductList, getProductSkuList, saveBundleItems, getBundleItems, getApprovedGuides } from '@/api/admin'
|
|
|
import { saveDimensionWeights, getDimensionWeights } from '@/api/dimension-weight'
|
|
import { saveDimensionWeights, getDimensionWeights } from '@/api/dimension-weight'
|
|
|
import DimensionWeightPicker from '@/components/DimensionWeightPicker.vue'
|
|
import DimensionWeightPicker from '@/components/DimensionWeightPicker.vue'
|
|
|
import ProductSkuEditor from './components/ProductSkuEditor.vue'
|
|
import ProductSkuEditor from './components/ProductSkuEditor.vue'
|
|
@@ -350,7 +348,10 @@ export default {
|
|
|
sortOrder: 0
|
|
sortOrder: 0
|
|
|
},
|
|
},
|
|
|
bundleItems: [], // 套餐组成: [{childProductId, childSkuId, quantity, productOptions:[], skuOptions:[]}]
|
|
bundleItems: [], // 套餐组成: [{childProductId, childSkuId, quantity, productOptions:[], skuOptions:[]}]
|
|
|
- servicePersons: [], // 线上服务者(deliveryMethod=4): [{userId,name,title,intro,avatar,price,originalPrice,slots:[{startTime,endTime,maxCapacity}]}]
|
|
|
|
|
|
|
+ servicePersons: [], // 线上服务者(deliveryMethod=4): [{userId,name,title,intro,avatar,price,originalPrice}]
|
|
|
|
|
+ approvedGuides: [], // 已入驻服务商列表
|
|
|
|
|
+ selectedGuideId: '',
|
|
|
|
|
+ guideSearchQuery: '',
|
|
|
onlineSlots: [], // 单人线上时段(deliveryMethod=5): [{startTime,endTime,maxCapacity}]
|
|
onlineSlots: [], // 单人线上时段(deliveryMethod=5): [{startTime,endTime,maxCapacity}]
|
|
|
growthArchive: {
|
|
growthArchive: {
|
|
|
enabled: false,
|
|
enabled: false,
|
|
@@ -485,9 +486,22 @@ export default {
|
|
|
selectedSystem() {
|
|
selectedSystem() {
|
|
|
if (!this.form.distributionSystemId || !this.supplySystems.length) return null
|
|
if (!this.form.distributionSystemId || !this.supplySystems.length) return null
|
|
|
return this.supplySystems.find(s => s.id === this.form.distributionSystemId)
|
|
return this.supplySystems.find(s => s.id === this.form.distributionSystemId)
|
|
|
|
|
+ },
|
|
|
|
|
+ filteredGuides() {
|
|
|
|
|
+ if (!this.approvedGuides.length) return this.approvedGuides
|
|
|
|
|
+ if (!this.guideSearchQuery) return this.approvedGuides
|
|
|
|
|
+ var q = this.guideSearchQuery.toLowerCase()
|
|
|
|
|
+ return this.approvedGuides.filter(function(g) {
|
|
|
|
|
+ return (g.realName || '').toLowerCase().indexOf(q) !== -1
|
|
|
|
|
+ || (g.nickname || '').toLowerCase().indexOf(q) !== -1
|
|
|
|
|
+ || (g.title || '').toLowerCase().indexOf(q) !== -1
|
|
|
|
|
+ || (g.specialty || '').toLowerCase().indexOf(q) !== -1
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
created() {
|
|
created() {
|
|
|
|
|
+ this.loadApprovedGuides()
|
|
|
|
|
+ },
|
|
|
this.editorConfig.MENU_CONF.uploadImage.server = this.uploadActionUrl
|
|
this.editorConfig.MENU_CONF.uploadImage.server = this.uploadActionUrl
|
|
|
this.editorConfig.MENU_CONF.uploadImage.headers = this.uploadHeaders
|
|
this.editorConfig.MENU_CONF.uploadImage.headers = this.uploadHeaders
|
|
|
},
|
|
},
|
|
@@ -679,12 +693,7 @@ async loadGiftRule(productId) {
|
|
|
intro: p.intro || '',
|
|
intro: p.intro || '',
|
|
|
avatar: p.avatar || '',
|
|
avatar: p.avatar || '',
|
|
|
price: (p.price != null) ? p.price / 100 : null,
|
|
price: (p.price != null) ? p.price / 100 : null,
|
|
|
- originalPrice: (p.originalPrice != null) ? p.originalPrice / 100 : null,
|
|
|
|
|
- slots: (p.slots || []).map(s => ({
|
|
|
|
|
- startTime: (s.startTime || '').replace('T', ' ').substring(0, 19),
|
|
|
|
|
- endTime: (s.endTime || '').replace('T', ' ').substring(0, 19),
|
|
|
|
|
- maxCapacity: s.maxCapacity || 1
|
|
|
|
|
- }))
|
|
|
|
|
|
|
+ originalPrice: (p.originalPrice != null) ? p.originalPrice / 100 : null
|
|
|
}))
|
|
}))
|
|
|
}
|
|
}
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
@@ -706,17 +715,11 @@ async loadGiftRule(productId) {
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
addServicePerson() {
|
|
addServicePerson() {
|
|
|
- this.servicePersons.push({ userId: '', name: '', title: '', intro: '', avatar: '', price: null, originalPrice: null, slots: [] })
|
|
|
|
|
|
|
+ this.servicePersons.push({ userId: '', name: '', title: '', intro: '', avatar: '', price: null, originalPrice: null })
|
|
|
},
|
|
},
|
|
|
removeServicePerson(idx) {
|
|
removeServicePerson(idx) {
|
|
|
this.servicePersons.splice(idx, 1)
|
|
this.servicePersons.splice(idx, 1)
|
|
|
},
|
|
},
|
|
|
- addServicePersonSlot(pIdx) {
|
|
|
|
|
- this.servicePersons[pIdx].slots.push({ startTime: '', endTime: '', maxCapacity: 1 })
|
|
|
|
|
- },
|
|
|
|
|
- removeServicePersonSlot(pIdx, sIdx) {
|
|
|
|
|
- this.servicePersons[pIdx].slots.splice(sIdx, 1)
|
|
|
|
|
- },
|
|
|
|
|
addOnlineSlot() {
|
|
addOnlineSlot() {
|
|
|
this.onlineSlots.push({ startTime: '', endTime: '', maxCapacity: 1 })
|
|
this.onlineSlots.push({ startTime: '', endTime: '', maxCapacity: 1 })
|
|
|
},
|
|
},
|
|
@@ -734,12 +737,7 @@ async loadGiftRule(productId) {
|
|
|
intro: p.intro || '',
|
|
intro: p.intro || '',
|
|
|
avatar: p.avatar || '',
|
|
avatar: p.avatar || '',
|
|
|
price: (p.price != null) ? Math.round(p.price * 100) : 0,
|
|
price: (p.price != null) ? Math.round(p.price * 100) : 0,
|
|
|
- originalPrice: (p.originalPrice != null) ? Math.round(p.originalPrice * 100) : null,
|
|
|
|
|
- slots: (p.slots || []).filter(s => s.startTime && s.endTime).map(s => ({
|
|
|
|
|
- startTime: s.startTime,
|
|
|
|
|
- endTime: s.endTime,
|
|
|
|
|
- maxCapacity: s.maxCapacity || 1
|
|
|
|
|
- }))
|
|
|
|
|
|
|
+ originalPrice: (p.originalPrice != null) ? Math.round(p.originalPrice * 100) : null
|
|
|
}))
|
|
}))
|
|
|
},
|
|
},
|
|
|
buildOnlineSlotPayload() {
|
|
buildOnlineSlotPayload() {
|
|
@@ -750,6 +748,34 @@ async loadGiftRule(productId) {
|
|
|
maxCapacity: s.maxCapacity || 1
|
|
maxCapacity: s.maxCapacity || 1
|
|
|
}))
|
|
}))
|
|
|
},
|
|
},
|
|
|
|
|
+ async loadApprovedGuides() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ var res = await getApprovedGuides()
|
|
|
|
|
+ if (res.code === 200 && Array.isArray(res.data)) {
|
|
|
|
|
+ this.approvedGuides = res.data
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.warn('加载服务商列表失败', e)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ onSelectGuide(userId) {
|
|
|
|
|
+ if (!userId) return
|
|
|
|
|
+ var guide = this.approvedGuides.find(function(g) { return g.userId == userId })
|
|
|
|
|
+ if (!guide) return
|
|
|
|
|
+ this.servicePersons.push({
|
|
|
|
|
+ userId: String(userId),
|
|
|
|
|
+ name: guide.realName || guide.nickname || '',
|
|
|
|
|
+ title: guide.title || '',
|
|
|
|
|
+ intro: guide.intro || guide.bio || '',
|
|
|
|
|
+ avatar: guide.avatar || '',
|
|
|
|
|
+ price: null,
|
|
|
|
|
+ originalPrice: null
|
|
|
|
|
+ })
|
|
|
|
|
+ this.selectedGuideId = ''
|
|
|
|
|
+ },
|
|
|
|
|
+ addSelectedGuide() {
|
|
|
|
|
+ this.onSelectGuide(this.selectedGuideId)
|
|
|
|
|
+ },
|
|
|
handlePurchaseFieldsToggle(val) {
|
|
handlePurchaseFieldsToggle(val) {
|
|
|
if (!val) {
|
|
if (!val) {
|
|
|
this.predefinedFields.forEach(pf => { pf.selected = false })
|
|
this.predefinedFields.forEach(pf => { pf.selected = false })
|