This commit is contained in:
全栈小学生 2026-03-20 15:59:46 +08:00
parent 504b4c96a8
commit 0c0e65be1c
228 changed files with 13911 additions and 2618 deletions

View File

@ -211,13 +211,23 @@ export function setShopDeliveryConfig(params: Record<string, any>) {
return request.put(`shop/delivery/setConfig`, params, { showSuccessMessage: true })
}
/********************************* 配送员 ***************************************/
/**
*
* @param params
* @returns
*/
export function getShopDelivery(params: Record<string, any>) {
return request.get('shop/delivery/staff', { params })
return request.get('shop/shop_delivery/staff', { params })
}
/**
*
* @param params
* @returns
*/
export function getShopDeliverList(params: Record<string, any>) {
return request.get('shop/shop_delivery/staff/list', { params })
}
/**
@ -226,7 +236,7 @@ export function getShopDelivery(params: Record<string, any>) {
* @returns
*/
export function getShopDeliverInfo(staff_id: number) {
return request.get(`shop/delivery/staff/${ staff_id }`);
return request.get(`shop/shop_delivery/staff/${ staff_id }`);
}
/**
@ -235,7 +245,7 @@ export function getShopDeliverInfo(staff_id: number) {
* @returns
*/
export function addShopDeliver(params: Record<string, any>) {
return request.post('shop/delivery/staff', params, { showSuccessMessage: true })
return request.post('shop/shop_delivery/staff', params, { showSuccessMessage: true })
}
/**
@ -244,7 +254,7 @@ export function addShopDeliver(params: Record<string, any>) {
* @returns
*/
export function editShopDeliver(params: Record<string, any>) {
return request.put(`shop/delivery/staff/${ params.deliver_id }`, params, { showSuccessMessage: true })
return request.put(`shop/shop_delivery/staff/${ params.deliver_id }`, params, { showSuccessMessage: true })
}
/**
@ -253,15 +263,48 @@ export function editShopDeliver(params: Record<string, any>) {
* @returns
*/
export function deleteShopDeliver(staff_id: number) {
return request.delete(`shop/delivery/staff/${ staff_id }`)
return request.delete(`shop/shop_delivery/staff/${ staff_id }`)
}
/********************************* 配送服务商 ***************************************/
/**
*
* @param params
* @returns
*/
export function getDeliveryServiceList(id: number) {
return request.get(`shop/delivery_store/delivery_service_list/${id}`)
}
/**
*
* @param params
* @returns
*/
export function deliveryShopOpen(params: Record<string, any>) {
return request.put(`shop/delivery_store/delivery_shop_open/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function deliveryShopEdit(params: Record<string, any>) {
return request.put(`shop/delivery_store/delivery_shop_edit/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/********************************* 同城配送设置 ***************************************/
/**
*
* @returns
*/
export function getLocal() {
return request.get('shop/local');
return request.get('shop/local_delivery/config');
}
/**
@ -270,7 +313,42 @@ export function getLocal() {
* @returns
*/
export function setLocal(params: Record<string, any>) {
return request.put('shop/local', params, { showSuccessMessage: true })
return request.put('shop/local_delivery/config', params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getInUseLocalDeliveryList(params: Record<string, any>) {
return request.get('shop/local_delivery/list', {params})
}
/**
*
*/
export function getLocalDeliveryServiceList() {
return request.get(`shop/local_delivery/service`)
}
/**
*
* @param params
* @returns
*/
export function setLocalDeliveryService(params: Record<string, any>) {
return request.put(`shop/local_delivery/service/${params.delivery_type}`, params, {showSuccessMessage: true})
}
/**
*
* @param delivery_type
* @returns
*/
export function getLocalDeliveryServiceInfo(delivery_type: string) {
return request.get(`shop/local_delivery/service/${delivery_type}`,)
}
/**
@ -280,3 +358,218 @@ export function setLocal(params: Record<string, any>) {
export function getThird() {
return request.get('shop/third/init')
}
/********************************* 同城配送记录 ***************************************/
/**
*
* @param params
* @returns
*/
export function getLocalDeliveryStatus() {
return request.get('shop/local_delivery/order/status')
}
/**
*
* @param params
* @returns
*/
export function getLocalDeliveryList(params: Record<string, any>) {
return request.get(`shop/local_delivery/order`, {params})
}
/**
*
* @param id
* @returns
*/
export function getLocalDeliveryInfo(id: number) {
return request.get(`shop/local_delivery/order/${ id }`);
}
/**
*
* @param params
* @returns
*/
export function getLocalDeliveryCancelReason(params: Record<string, any>) {
return request.get('shop/local_delivery/order/cancel_reason', {params})
}
/**
*
* @param staff_id
* @returns
*/
export function getLocalDeliverySync(id: number) {
return request.put(`shop/local_delivery/order/sync/${ id }`);
}
/**
*
* @param params
* @returns
*/
export function setLocalDeliveryCancel(params: Record<string, any>) {
return request.put(`shop/local_delivery/order/cancel/${ params.id }`, params, { showSuccessMessage: true })
}
/********************************* 商家配送记录 ***************************************/
/**
*
* @param params
* @returns
*/
export function getShopDeliveryStatus() {
return request.get('shop/shop_delivery/order/status')
}
/**
*
* @param params
* @returns
*/
export function getShopDeliveryOrderList(params: Record<string, any>) {
return request.get(`shop/shop_delivery/order`, {params})
}
/**
*
* @param id
* @returns
*/
export function getShopDeliveryInfo(id: number) {
return request.get(`shop/shop_delivery/order/${ id }`);
}
/**
*
* @param staff_id
* @returns
*/
export function getShopDeliveryFinish(id: number) {
return request.put(`shop/shop_delivery/order/finish/${ id }`);
}
/**
*
* @param staff_id
* @returns
*/
export function getDeliveryFinish(id: number) {
return request.put(`shop/local_delivery/order/finish/${ id }`);
}
/**
*
* @param params
* @returns
*/
export function setShopDeliveryCancel(params: Record<string, any>) {
return request.put(`shop/shop_delivery/order/cancel/${ params.id }`, params, { showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function getShopDeliveryType() {
return request.get(`shop/delivery/type`);
}
/********************************* 门店自提 ***************************************/
/**
*
* @param params
* @returns
*/
export function getDeliveryStoreList(params: Record<string, any>) {
return request.get(`shop/delivery/store`, {params})
}
/**
*
* @param params
* @returns
*/
export function getDeliveryStoreListAll(params: Record<string, any>) {
return request.get(`shop/delivery/store/list`, {params})
}
/**
*
* @returns
*/
export function getStorePickUpType() {
return request.get(`shop/delivery/store/pick_up_type`);
}
/**
*
* @returns
*/
export function getDeliveryStoreDetail(id: number) {
return request.get(`shop/delivery/store/${ id }`);
}
/**
*
* @param params
* @returns
*/
export function addDeliveryStore(params: Record<string, any>) {
return request.post('shop/delivery/store', params, {showErrorMessage: true, showSuccessMessage: true})
}
/**
*
* @param params
* @returns
*/
export function editDeliveryStore(params: Record<string, any>) {
return request.put(`shop/delivery/store/${params.store_id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param template_id
* @returns
*/
export function deleteDeliveryStore(store_id: number) {
return request.delete(`shop/delivery/store/${store_id}`, {showErrorMessage: true, showSuccessMessage: true})
}
/**
*
* @param params
* @returns
*/
export function editDeliveryStoreStatus(params: Record<string, any>) {
return request.put(`shop/delivery/store/modify_status`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getDeliveryConfig() {
return request.get('shop/local_delivery/base_config')
}
/**
*
* @param params
* @returns
*/
export function setDeliveryConfig(params: Record<string, any>) {
return request.put('shop/local_delivery/base_config', params, {showSuccessMessage: true})
}

View File

@ -129,7 +129,7 @@ export function editGoodsStatus(params: Record<string, any>) {
* @param params
*/
export function copyGoods(params: Record<string, any>) {
return request.put(`shop/goods/copy/${ params.goods_id }`, params, { showSuccessMessage: true })
return request.put(`shop/goods/copy/${ params.goods_id }`, params)
}
/**

View File

@ -279,3 +279,19 @@ export function getOrderBatchDeliveryType() {
export function closeRefund(order_refund_no: number) {
return request.put(`shop/order/refund/close/${order_refund_no}`)
}
/**
*
* @return
*/
export function getSelectOrderGoodsWeight(params: Record<string, any>) {
return request.get(`shop/order/select/weight`, {params})
}
/**
*
* @return
*/
export function getDeliveryFee(params: Record<string, any>) {
return request.get(`shop/order/delivery/fee`, {params})
}

View File

@ -87,5 +87,6 @@
"notLessThanZero": "不能小于0",
"mustBeGreaterThanZero": "必须大于0",
"thridRequire": "请选择一个三方配送服务",
"thridSeting": "请填写完整的三方配送配置"
"thridSeting": "请填写完整的三方配送配置",
"deliveryArea": "配送区域"
}

View File

@ -0,0 +1,4 @@
{
"isStoreSelect":"是否开启门店选择",
"isShowPolyline":"是否显示配送轨迹"
}

View File

@ -0,0 +1,12 @@
{
"name": "三方配送类型",
"dadaThirdDelivery":"达达秒送",
"isUse":"是否启用",
"config":"设置",
"dadaAppKey": "APP_KEY",
"dadaAppSecret":"APP_SECRET",
"dadaSourceId": "商户ID",
"dadaAppKeyPlaceholder": "请输入APP_KEY",
"dadaAppSecretPlaceholder":"请输入APP_SECRET",
"dadaSourceIdPlaceholder":"请输入商户ID"
}

View File

@ -0,0 +1,33 @@
{
"deliveryNo": "配送单号",
"tradeNo": "交易单号",
"tradeNoPlaceholder":"请输入交易单号",
"deliveryNoPlaceholder":"请输入配送单号",
"createTime":"配送时间",
"startDate":"开始日期",
"endDate":"结束日期",
"deliveryServiceName":"配送服务名称",
"goodsInfo":"商品信息",
"deliveryStart":"配送起点",
"deliveryEnd":"配送终点",
"deliveryStatus":"状态",
"deliveryStatusPlaceholder":"请选择状态",
"deliveryDistance":"配送距离",
"deliveryMoney":"配送费用",
"remark":"备注",
"detail": "详情",
"sync":"同步",
"syncTips":"您确定要同步配送订单吗?",
"finish":"完成",
"finishTips":"您确定要完成配送订单吗?",
"cancelTime":"取消时间",
"riderName":"配送员姓名",
"riderMobile":"配送员手机号",
"outDeliveryNo":"外部配送单号",
"deliveryCancel":"取消配送",
"cancelReasonSelectPlaceholder":"请选择取消原因类型",
"cancelReasonType":"取消原因类型",
"cancelReason":"取消原因",
"cancelReasonPlaceholder":"请输入取消原因"
}

View File

@ -0,0 +1,31 @@
{
"deliveryNo": "配送单号",
"tradeNo": "交易单号",
"tradeNoPlaceholder":"请输入交易单号",
"deliveryNoPlaceholder":"请输入配送单号",
"createTime":"配送时间",
"startDate":"开始日期",
"endDate":"结束日期",
"deliveryServiceName":"配送服务名称",
"goodsInfo":"商品信息",
"deliveryStart":"配送起点",
"deliveryEnd":"配送终点",
"deliveryStatus":"状态",
"deliveryStatusPlaceholder":"请选择状态",
"deliveryDistance":"配送距离",
"deliveryMoney":"配送费用",
"remark":"备注",
"detail": "详情",
"finish":"完成",
"finishTips":"您确定要完成配送订单吗?",
"cancelTime":"取消时间",
"riderName":"配送员姓名",
"riderMobile":"配送员手机号",
"outDeliveryNo":"外部配送单号",
"deliveryCancel":"取消配送",
"cancelReasonSelectPlaceholder":"请选择取消原因类型",
"cancelReasonType":"取消原因类型",
"cancelReason":"取消原因",
"cancelReasonPlaceholder":"请输入取消原因"
}

View File

@ -0,0 +1,12 @@
{
"deliveryServiceName": "服务商",
"openService": "开通",
"editService": "修改品类",
"business": "门店品类",
"businessPlaceholder": "请选择门店品类",
"openStatus": "开通状态",
"editStatus": "门店信息变更状态",
"reason": "备注",
"pass": "通过",
"notPass": "未通过"
}

View File

@ -0,0 +1,103 @@
{
"basicSettings": "同城配送基础设置",
"timeIsOpen": "配送时间设置",
"timeIsOpenTips": "开启后,买家下单选择同城配送时,可选择配送时间,提交订单后,将在买家备注中显示。关闭后,买家下单默认为立即配送",
"close": "关闭",
"open": "开启",
"everyDay": "每天",
"monday": "周一",
"tuesday": "周二",
"wednesday": "周三",
"thursday": "周四",
"friday": "周五",
"saturday": "周六",
"sunday": "周日",
"timeWeekRequire": "请选择配送时间",
"deliveryTimeSetting": "配送时间设置",
"feeType": "收费标准",
"region": "按区域收取配送费",
"distance": "按距离收取配送费",
"district": "按行政区域收取配送费",
"feeSetting": "费用设置",
"weightFee": "续重收费",
"feeSettingTextOne": "km内按",
"feeSettingTextTwo": "元收取配送费,每超出",
"feeSettingTextThree": "km费用增加",
"priceUnit": "元",
"weightFeeTextOne": "商品重量",
"weightFeeTextTwo": "kg 内不额外收费,每超出",
"weightFeeTextThree": "kg 费用增加",
"areaName": "区域名称",
"startPrice": "起送价",
"deliveryPrice": "配送费",
"areaType": "划分方式",
"radius": "半径",
"custom": "自定义",
"addDeliveryArea": "添加配送区域",
"baseDistRequire": "请输入起始公里数",
"gradDistRequire": "请输入超出公里数",
"basePriceRequire": "请输入起始公里内的配送费用",
"gradPriceRequire": "请输入每超出公里部分的费用",
"areaNameRequire": "请输入区域名称",
"startPriceRequire": "请输入起送价",
"startPriceMin": "起送价不能小于0",
"deliveryPriceRequire": "请输入配送费",
"deliveryPriceMin": "配送费不能小于0",
"areaPlaceholder": "请添加配送区域",
"deliveryType": "配送方式",
"business": "商家自配送",
"deliveryTypeRequire": "至少需选择一种配送方式",
"deliveryAddress": "取货地址",
"defaultDeliveryAddressEmpty": "请先配置默认发货地址",
"toSetting": "去配置",
"update": "修改",
"deliveryAddressChange": "取货地址已变更请注意是否需重新调整配送区域",
"tradeTimePlaceholderTwo": "请选择提货时段",
"tradeTimePlaceholderThree": "请选择细分时段",
"tradeTimePlaceholderFour": "结束时间不能小于或等于开始时间",
"tradeTimePlaceholderFive": "后一个时间段的开始时间不能小于前一个时间段的结束时间",
"thrid":"三方配送",
"AppKey":"AppKey",
"AppSecret":"AppSecret",
"shopId":"商户ID",
"shopStoreNo":"商户门店编号",
"AppKeyRequire":"请输入AppKey",
"AppSecretRequire":"请输入AppSecret",
"shopIdRequire":"请输入商户ID",
"shopStoreNoRequire":"请输入商户门店编号",
"deliveryTime":"配送时段设置",
"startTime":"开始时间",
"endTime":"结束时间",
"timeInterval":"细分时段",
"addTime":"添加配送时段",
"deliveryTimeTips":"配送时段设置,若不在配送时段内,则无法进行配送",
"30minute": "30分钟",
"90minute": "90分钟",
"oneHour": "一小时",
"twoHour": "两小时",
"advancaDay": "提前预约",
"advance": "提前",
"day": "天",
"reservationAvailable":"可预约",
"withinDays":"天内",
"advanceTips": "若设置了提前预约,则用户在当前日期前多少天可以预约配送,不可以立即配送",
"mostDays": "最长预约",
"mostDaysTips": "预约配送最长可预约多少天内进行提货",
"formatError": "格式不正确",
"notLessThanZero": "不能小于0",
"mustBeGreaterThanZero": "必须大于0",
"thridRequire": "请选择一个三方配送服务",
"thridSeting": "请填写完整的三方配送配置",
"addressPlaceholder":"请输入详细地址",
"fullAddressPlaceholder":"请输入地址",
"storeName": "提货点名称",
"storeNamePlaceholder": "请输入提货点名称",
"contactName": "联系人姓名",
"contactNamePlaceholder": "请输入联系人姓名",
"storeMobile": "联系人电话",
"storeMobilePlaceholder": "请输入联系人电话",
"tradeTime": "营业时间",
"tradeTimePlaceholder": "请输入营业时间",
"tradeTimeTips": "例上午9:00-12:00下午2:00-6:00",
"deliveryStoreTips": "当前提货点为系统自建提货点,无法删除且提货点名称、联系人电话、营业时间及地址只能在店铺设置中统一配置。"
}

View File

@ -116,6 +116,22 @@
"refundInstructionsOne": "1、如果是退部分金额退款后可以是部分退款状态或退款完成状态",
"refundInstructionsTwo": "2、如果是退全部金额则退款后一定是退款完成状态",
"refundInstructionsThree": "3、退款完成才会执行相关业务如核销码失效卡包失效等操作",
"storeDeliveryWay": "商家配送",
"thirdPartyDeliveryWay": "第三方配送",
"deliverName": "配送员",
"deliverNamePlaceholder": "请选择配送员",
"deliverMobile": "配送员手机号",
"deliverMobilePlaceholder": "请输入配送员手机号",
"thirdDeliveryName": "配送平台",
"thirdDeliveryNamePlaceholder": "请选择配送平台",
"goodsWeight": "商品重量",
"goodsWeightPlaceholder": "请输入商品重量",
"weightTips": "[商品重量(kg)]格式输入错误",
"weightNotZeroTips": "商品重量(kg)必须大于0",
"deliveryAddress": "发货地址",
"deliveryPlatform": "配送平台",
"deliveryStatus": "配送状态",
"modifyDelivery": "修改发货信息",
"backRefund": "原路退款",
"offlineRefund": "线下退款",
"refundToBalance": "退款到余额",

View File

@ -160,5 +160,22 @@
"orderInfoEmpty": "暂无数据",
"buyerAskDeliveryTime": "要求自提时间",
"deleteTips": "是否确认删除该订单?",
"batchDeleteTips": "是否确认批量删除选中的订单?"
"batchDeleteTips": "是否确认批量删除选中的订单?",
"storeDeliveryWay": "商家配送",
"thirdPartyDeliveryWay": "第三方配送",
"deliverName": "配送员",
"deliverNamePlaceholder": "请选择配送员",
"deliverMobile": "配送员手机号",
"deliverMobilePlaceholder": "请输入配送员手机号",
"thirdDeliveryName": "配送平台",
"thirdDeliveryNamePlaceholder": "请选择配送平台",
"goodsWeight": "商品重量",
"goodsWeightPlaceholder": "请输入商品重量",
"weightTips": "[商品重量(kg)]格式输入错误",
"weightNotZeroTips": "商品重量(kg)必须大于0",
"deliveryStore": "提货点",
"deliveryStorePlaceholder": "请选择提货点",
"deliveryPlatform": "配送平台",
"deliveryStatus": "配送状态",
"goActive":"去开通"
}

View File

@ -117,7 +117,7 @@ const router = useRouter()
* 添加商家地址库
*/
const addEvent = () => {
router.push('/shop/order/address/edit')
router.push('/shop/address/edit')
}
/**
@ -125,7 +125,7 @@ const addEvent = () => {
* @param data
*/
const editEvent = (data: any) => {
router.push('/shop/order/address/edit?id=' + data.id)
router.push('/shop/address/edit?id=' + data.id)
}
/**

View File

@ -2,7 +2,7 @@
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push('/shop/order/delivery')" />
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push('/shop/delivery/config')" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
@ -118,7 +118,7 @@ loadCompanyList(getTablePageStorage(companyTable.searchParam).page);
* 添加物流公司
*/
const addEvent = () => {
router.push('/shop/order/delivery/company_add')
router.push('/shop/delivery/company_add')
}
/**
@ -126,7 +126,7 @@ const addEvent = () => {
* @param data
*/
const editEvent = (data: any) => {
router.push('/shop/order/delivery/company_edit?company_id=' + data.company_id)
router.push('/shop/delivery/company_edit?company_id=' + data.company_id)
}
/**

View File

@ -135,7 +135,7 @@ const router = useRouter()
const loading = ref(true)
const back = () => {
router.push('/shop/order/delivery/company')
router.push('/shop/delivery/company')
}
/**
@ -301,7 +301,7 @@ const save = async(formEl: FormInstance | undefined) => {
const data = formData
api(data).then(res => {
router.push('/shop/order/delivery/company')
router.push('/shop/delivery/company')
repeat.value = false
}).catch(() => {
repeat.value = false

View File

@ -0,0 +1,118 @@
<template>
<el-dialog v-model="showDialog" :title="t('dadaThirdDelivery')" width="580px" :destroy-on-close="true">
<el-form :model="formData" label-width="140px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('isUse')">
<el-radio-group v-model="formData.is_use">
<el-radio :label="1">{{ t('startUsing') }}</el-radio>
<el-radio :label="0">{{ t('statusDeactivate') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('dadaAppKey')" prop="app_key">
<el-input v-model.trim="formData.app_key" :placeholder="t('dadaAppKeyPlaceholder')" class="input-width" show-word-limit clearable />
</el-form-item>
<el-form-item :label="t('dadaAppSecret')" prop="app_secret">
<el-input v-model.trim="formData.app_secret" :placeholder="t('dadaAppSecretPlaceholder')" class="input-width" clearable />
</el-form-item>
<el-form-item :label="t('dadaSourceId')" prop="source_id">
<el-input v-model.trim="formData.source_id" :placeholder="t('dadaSourceIdPlaceholder')" class="input-width" clearable />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{t('confirm')}}</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { setLocalDeliveryService, getLocalDeliveryServiceInfo } from '@/addon/shop/api/delivery'
const showDialog = ref(false)
const loading = ref(true)
/**
* 表单数据
*/
const initialFormData = {
delivery_type: '',
app_key: '',
app_secret: '',
source_id: '',
is_use: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
app_key: [
{ required: true, message: t('dadaAppKeyPlaceholder'), trigger: 'blur' }
],
app_secret: [
{ required: true, message: t('dadaAppSecretPlaceholder'), trigger: 'blur' }
],
source_id: [
{ required: true, message: t('dadaSourceIdPlaceholder'), trigger: 'blur' }
]
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
const data = formData
setLocalDeliveryService(data).then(res => {
loading.value = false
showDialog.value = false
emit('complete')
}).catch(() => {
loading.value = false
// showDialog.value = false
})
}
})
}
const setFormData = async (row: any = null) => {
loading.value = true
Object.assign(formData, initialFormData)
if (row) {
const data = await (await getLocalDeliveryServiceInfo(row.delivery_type)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
if (data.params[key] != undefined) formData[key] = data.params[key].value
})
}
loading.value = false
}
defineExpose({
showDialog,
setFormData
})
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,141 @@
<template>
<el-drawer v-model="showDialog" :title="popTitle" direction="rtl" :before-close="handleClose" class="local-record-detail-drawer">
<div class="main-container" v-loading="loading">
<el-form :model="detailData" label-width="100px" ref="formRef" class="page-form" label-position="left">
<div class="row-bg px-[20px] mb-[20px] multiple-layout">
<el-form-item :label="t('deliveryServiceName')">
<div class="input-width">{{ detailData.delivery_service_name }}</div>
</el-form-item>
<el-form-item :label="t('goodsInfo')">
<div class="input-width">{{ detailData.body }}</div>
</el-form-item>
<el-form-item :label="t('deliveryNo')">
<div class="input-width">{{ detailData.delivery_no }}</div>
</el-form-item>
<el-form-item :label="t('tradeNo')">
<div class="input-width text-primary cursor-pointer" @click="toLink">{{ detailData.trade_no }}</div>
</el-form-item>
<el-form-item :label="t('deliveryStart')">
<div class="input-width">{{ detailData.delivery_start }}</div>
</el-form-item>
<el-form-item :label="t('deliveryEnd')">
<div class="input-width">{{ detailData.delivery_end }}</div>
</el-form-item>
<el-form-item :label="t('deliveryStatus')">
<div class="input-width">{{ detailData.status_name }}</div>
</el-form-item>
<el-form-item :label="t('deliveryDistance')">
<div class="input-width">{{ distance(detailData.delivery_distance) }}</div>
</el-form-item>
<el-form-item :label="t('deliveryMoney')">
<div class="input-width">{{ detailData.delivery_money }}</div>
</el-form-item>
<el-form-item :label="t('cancelTime')" v-if="detailData.cancel_time">
<div class="input-width">{{ detailData.cancel_time }}</div>
</el-form-item>
<el-form-item :label="t('riderName')" v-if="detailData.rider_name">
<div class="input-width">{{ detailData.rider_name }}</div>
</el-form-item>
<el-form-item :label="t('riderMobile')" v-if="detailData.rider_mobile">
<div class="input-width">{{ detailData.rider_mobile }}</div>
</el-form-item>
<el-form-item :label="t('outDeliveryNo')">
<div class="input-width">{{ detailData.out_delivery_no }}</div>
</el-form-item>
<el-form-item :label="t('remark')">
<div class="input-width">{{ detailData.remark }}</div>
</el-form-item>
</div>
</el-form>
<div>
<h3 class="mt-[50px] mb-[20px]" v-if="detailData.orderLog.length > 0">配送日志</h3>
<div class="mb-[100px]" style="min-height: 100px" v-if="detailData.orderLog.length > 0">
<div class="flex" v-for="(items, index) in detailData.orderLog" :key="index">
<div class="mr-[20px] min-w-[71px]">
<div class="leading-[1] w-full text-[14px] w-[100px] flex justify-end">
{{ items.create_time && items.create_time.split(' ')[0] }}
</div>
<div class="leading-[1] w-full text-[14px] w-[100px] flex justify-end mt-[15px]">
{{ items.create_time && items.create_time.split(' ')[1] }}
</div>
</div>
<div>
<div class="w-[16px] h-[16px] flex items-center bg-[#D1EBFF] border-[1px] border-[#0091FF] rounded-[999px]">
<div class="w-[8px] h-[8px] mx-auto bg-[#0091FF] rounded-[999px]"></div>
</div>
<div v-if="(index + 1) != detailData.orderLog.length" class="w-[2px] h-[50px] bg-[#D1EBFF] mx-auto">
</div>
</div>
<div>
<div class="leading-[1] ml-[20px] text-[14px]">
{{ items.main_type_name }}{{ items.main_name }}
</div>
<div class="leading-[1] ml-[20px] text-[14px] mt-[15px]">
<span>{{ items.remark }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</el-drawer>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { t } from '@/lang'
import { distance } from '@/utils/common'
import { getLocalDeliveryInfo } from '@/addon/shop/api/delivery'
import { useRouter } from 'vue-router'
const router = useRouter()
const showDialog = ref(false)
const loading = ref(false)
const popTitle: string = '配送详情'
const detailData = ref<any>({})
let id: any = ''
const handleClose = (done: () => void) => {
showDialog.value = false
}
const getLocalDeliveryInfoFn = async () => {
loading.value = true
if (id) {
detailData.value = await (await getLocalDeliveryInfo(id)).data
loading.value = false
} else {
loading.value = false
}
showDialog.value = true
}
const setFormData = async (row: any = null) => {
id = row.id
getLocalDeliveryInfoFn()
}
const toLink = () => {
const url = router.resolve({
path: '/shop/order/detail',
query: {
order_id: detailData.value.trade_id
}
})
window.open(url.href)
}
defineExpose({
showDialog,
setFormData
})
</script>
<style lang="scss">
.local-record-detail-drawer{
width: 1300px !important;
.input-width{
width: 280px;
}
}
</style>

View File

@ -0,0 +1,139 @@
<template>
<el-drawer v-model="showDialog" :title="popTitle" direction="rtl" :before-close="handleClose" class="local-record-detail-drawer">
<div class="main-container" v-loading="loading">
<el-form :model="detailData" label-width="100px" ref="formRef" class="page-form" label-position="left">
<div class="row-bg px-[20px] mb-[20px] multiple-layout">
<el-form-item :label="t('deliveryNo')">
<div class="input-width">{{ detailData.delivery_no }}</div>
</el-form-item>
<el-form-item :label="t('goodsInfo')">
<div class="input-width">{{ detailData.body }}</div>
</el-form-item>
<el-form-item :label="t('tradeNo')">
<div class="input-width text-primary cursor-pointer" @click="toLink">{{ detailData.trade_no }}</div>
</el-form-item>
<el-form-item :label="t('deliveryStart')">
<div class="input-width">{{ detailData.delivery_start }}</div>
</el-form-item>
<el-form-item :label="t('deliveryEnd')">
<div class="input-width">{{ detailData.delivery_end }}</div>
</el-form-item>
<el-form-item :label="t('deliveryStatus')">
<div class="input-width">{{ detailData.status_name }}</div>
</el-form-item>
<el-form-item :label="t('deliveryDistance')">
<div class="input-width">{{ distance(detailData.delivery_distance) }}</div>
</el-form-item>
<el-form-item :label="t('deliveryMoney')">
<div class="input-width">{{ detailData.delivery_money }}</div>
</el-form-item>
<el-form-item :label="t('cancelTime')" v-if="detailData.cancel_time">
<div class="input-width">{{ detailData.cancel_time }}</div>
</el-form-item>
<el-form-item :label="t('riderName')" v-if="detailData.rider_name">
<div class="input-width">{{ detailData.rider_name }}</div>
</el-form-item>
<el-form-item :label="t('riderMobile')" v-if="detailData.rider_mobile">
<div class="input-width">{{ detailData.rider_mobile }}</div>
</el-form-item>
<el-form-item :label="t('outDeliveryNo')">
<div class="input-width">{{ detailData.out_delivery_no }}</div>
</el-form-item>
<el-form-item :label="t('remark')">
<div class="input-width">{{ detailData.remark }}</div>
</el-form-item>
</div>
</el-form>
<div>
<h3 class="mt-[50px] mb-[20px]" v-if="detailData.orderLog.length > 0">配送日志</h3>
<div class="mb-[100px]" style="min-height: 100px" v-if="detailData.orderLog.length > 0">
<div class="flex" v-for="(items, index) in detailData.orderLog" :key="index">
<div class="mr-[20px] min-w-[71px]">
<div class="leading-[1] w-full text-[14px] w-[100px] flex justify-end">
{{ items.create_time && items.create_time.split(' ')[0] }}
</div>
<div class="leading-[1] w-full text-[14px] w-[100px] flex justify-end mt-[15px]">
{{ items.create_time && items.create_time.split(' ')[1] }}
</div>
</div>
<div>
<div class="w-[16px] h-[16px] flex items-center bg-[#D1EBFF] border-[1px] border-[#0091FF] rounded-[999px]">
<div class="w-[8px] h-[8px] mx-auto bg-[#0091FF] rounded-[999px]"></div>
</div>
<div v-if="index + 1 != detailData.orderLog.length" class="w-[2px] h-[50px] bg-[#D1EBFF] mx-auto">
</div>
</div>
<div>
<div class="leading-[1] ml-[20px] text-[14px]">
{{ items.main_type_name }}{{ items.main_name }}
</div>
<div class="leading-[1] ml-[20px] text-[14px] mt-[15px]">
<span>{{ items.remark }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</el-drawer>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { t } from '@/lang'
import { distance } from '@/utils/common'
import { getShopDeliveryInfo } from '@/addon/shop/api/delivery'
import { useRouter } from 'vue-router'
const router = useRouter()
const showDialog = ref(false)
const loading = ref(false)
const popTitle: string = '配送详情'
const detailData = ref<any>({})
let id: any = ''
const handleClose = (done: () => void) => {
showDialog.value = false
}
const getShopDeliveryInfoFn = async () => {
loading.value = true
if (id) {
detailData.value = await (await getShopDeliveryInfo(id)).data
loading.value = false
} else {
loading.value = false
}
showDialog.value = true
}
const setFormData = async (row: any = null) => {
id = row.id
getShopDeliveryInfoFn()
}
const toLink = () => {
const path = '/shop/order/detail'
const url = router.resolve({
path,
query: {
order_id: detailData.value.trade_id
}
})
window.open(url.href)
}
defineExpose({
showDialog,
setFormData
})
</script>
<style lang="scss">
.local-record-detail-drawer{
width: 1300px !important;
.input-width{
width: 280px;
}
}
</style>

View File

@ -25,16 +25,16 @@
<span class="text-[#666666] text-[14px]">{{ t(item.key) }}</span>
<div>
<template v-if="item.key === 'local_delivery'">
<el-button type="primary" link @click="goRouter('/shop/order/delivery/staff')">{{ t('deliveryStaff') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/order/delivery/local')">{{ t('localConfig') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/delivery/local_config')">基础配置</el-button>
<el-button type="primary" link @click="goRouter('/shop/delivery/local')">{{ t('localConfig') }}</el-button>
</template>
<template v-if="item.key === 'express'">
<el-button type="primary" link @click="goRouter('/shop/order/delivery/company')">{{ t('deliveryCompany') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/order/shipping/template')">{{ t('deliveryTemplate') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/order/delivery/search')">{{ t('deliverySearch') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/delivery/company')">{{ t('deliveryCompany') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/shipping/template')">{{ t('deliveryTemplate') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/delivery/search')">{{ t('deliverySearch') }}</el-button>
</template>
<template v-if="item.key === 'store'">
<el-button type="primary" link @click="goRouter('/shop/order/delivery/store')">{{ t('deliveryStore') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/delivery_store')">{{ t('deliveryStore') }}</el-button>
</template>
</div>
</div>

View File

@ -5,141 +5,7 @@
</el-card>
<el-card class="box-card !border-none" shadow="never">
<el-form label-width="120px" ref="formRef" :rules="formRules" :model="formData" class="page-form"
v-loading="loading">
<!-- <h3 class="panel-title">{{t('basicSettings')}}</h3> -->
<el-form-item :label="t('deliveryType')" prop="delivery_type">
<!-- <el-checkbox-group v-model="formData.delivery_type">
<el-checkbox label="business">{{ t('business') }}</el-checkbox>
<el-checkbox label="third">{{ t('三方配送') }}</el-checkbox>
</el-checkbox-group> -->
<el-radio-group v-model="formData.delivery_type">
<el-radio :label="'business'">{{ t('business') }}</el-radio>
<el-radio :label="'third'">{{ t('thrid') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('thrid')" prop="time_is_open" v-if="formData.delivery_type=='third'">
<div v-for="(service, index) in thirdPartyData" :key="service.type">
<div class="flex items-center mr-[20px]">
<span class="mr-[10px]">{{ service.name }}</span>
<el-switch v-model="service.isEnabled" @change="handleServiceChange(index)"></el-switch>
</div>
</div>
</el-form-item>
<template v-if="activeService && formData.delivery_type=='third'">
<el-form-item :label="t('AppKey')" prop="app_key">
<el-input v-model="activeService.config.app_key" clearable :placeholder="t('AppKeyRequire')" class="input-width" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item :label="t('AppSecret')" prop="app_secret">
<el-input v-model="activeService.config.app_secret" clearable :placeholder="t('AppSecretRequire')" class="input-width" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item :label="t('shopId')" prop="shop_id">
<el-input v-model="activeService.config.shop_id" clearable :placeholder="t('shopIdRequire')" class="input-width" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item :label="t('shopStoreNo')" prop="shop_store_no">
<el-input v-model="activeService.config.shop_store_no" clearable :placeholder="t('shopStoreNoRequire')" class="input-width" maxlength="100" show-word-limit />
</el-form-item>
</template>
<el-form-item :label="t('timeIsOpen')" prop="time_is_open">
<div>
<el-radio-group v-model="formData.time_is_open">
<el-radio :label="1">{{ t('open') }}</el-radio>
<el-radio :label="0">{{ t('close') }}</el-radio>
</el-radio-group>
<div class="mt-[10px] text-[12px] text-[#999] leading-[20px]">{{t('timeIsOpenTips')}}</div>
</div>
</el-form-item>
<template v-if="formData.time_is_open === 1">
<el-form-item>
<el-radio-group v-model="formData.time_type">
<el-radio :label="0">{{ t('everyDay') }}</el-radio>
<el-radio :label="1">{{ t('custom') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="time_week" v-if="formData.time_type===1">
<el-checkbox-group v-model="formData.time_week">
<el-checkbox label="1">{{ t('monday') }}</el-checkbox>
<el-checkbox label="2">{{ t('tuesday') }}</el-checkbox>
<el-checkbox label="3">{{ t('wednesday') }}</el-checkbox>
<el-checkbox label="4">{{ t('thursday') }}</el-checkbox>
<el-checkbox label="5">{{ t('friday') }}</el-checkbox>
<el-checkbox label="6">{{ t('saturday') }}</el-checkbox>
<el-checkbox label="0">{{ t('sunday') }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item :label="t('deliveryTime')" prop="delivery_time">
<div>
<div>
<div v-for="(timeRange, index) in formData.delivery_time" :key="index" class="mb-3">
<el-time-picker v-model="timeRange.start_time" :placeholder="t('startTime')"
format="HH:mm" value-format="HH:mm"
:picker-options="{selectableRange: '00:00 - 23:59'}" />
<span class="mx-2">-</span>
<el-time-picker v-model="timeRange.end_time" :placeholder="t('endTime')" format="HH:mm"
value-format="HH:mm" :picker-options="{selectableRange: '00:00 - 23:59'}" />
<span v-if="index > 0" class="text-primary cursor-pointer ml-[10px]"
@click="removeTimeRange(index)"> {{ t('delete') }}</span>
</div>
<span class="text-primary cursor-pointer mr-[10px]" @click="addTimeRange"
v-if="formData.delivery_time.length < 3"> {{ t('addTime') }}</span>
</div>
<div class="text-[12px] text-[#999]">{{ t('deliveryTimeTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('timeInterval')" prop="time_interval">
<div>
<el-radio-group v-model="formData.time_interval">
<el-radio :label="30">{{ t('30minute') }}</el-radio>
<el-radio :label="60">{{ t('oneHour') }}</el-radio>
<el-radio :label="90">{{ t('90minute') }}</el-radio>
<el-radio :label="120">{{ t('twoHour') }}</el-radio>
</el-radio-group>
<!-- <p class="text-[12px] text-[#999]">{{ t('storeTimeIntervalTips') }}</p> -->
</div>
</el-form-item>
<el-form-item :label="t('advancaDay')" prop="advance_day">
<div>
<div class="flex">
{{ t('advance') }}
<div class="w-[100px] mx-[5px]">
<el-input v-model.trim="formData.advance_day" />
</div>
{{ t('day') }}
</div>
<p class="text-[12px] text-[#999]">{{ t('advanceTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('mostDays')" prop="most_day">
<div>
<div class="flex">
{{ t('reservationAvailable') }}
<div class="w-[100px] mx-[5px]">
<el-input v-model.trim="formData.most_day" />
</div>
{{ t('withinDays') }}
</div>
<p class="text-[12px] text-[#999]">{{ t('mostDaysTips') }}</p>
</div>
</el-form-item>
</template>
<el-form-item :label="t('deliveryAddress')" prop="delivery_address">
<div class="flex flex-col">
<div class="flex">
{{ defaultDeliveryAddress ? defaultDeliveryAddress.full_address :
t('defaultDeliveryAddressEmpty') }}
<el-button type="primary" @click="router.push('/shop/order/address')" link
class="ml-[10px]">{{ defaultDeliveryAddress ? t('update') : t('toSetting')
}}</el-button>
</div>
<div class="text-error leading-none"
v-if="formData.center.lat && defaultDeliveryAddress && (formData.center.lat != defaultDeliveryAddress.lat || formData.center.lng != defaultDeliveryAddress.lng)">
{{ t('deliveryAddressChange') }}</div>
</div>
</el-form-item>
<el-form label-width="80px" ref="formRef" :rules="formRules" :model="formData" class="page-form" v-loading="loading">
<el-form-item :label="t('feeType')">
<el-radio-group v-model="formData.fee_type">
<el-radio label="region">{{ t('region') }}</el-radio>
@ -166,7 +32,7 @@
{{ t('priceUnit') }}
</div>
</el-form-item>
<el-form-item :label="t('weightFee')" prop="">
<el-form-item :label="t('weightFee')" prop="weight">
<div class="flex">
{{ t('weightFeeTextOne') }}
<div class="w-[60px] mx-[5px]">
@ -183,36 +49,53 @@
{{ t('priceUnit') }}
</div>
</el-form-item>
<el-form-item prop="area" v-loading="mapLoading">
<div class="relative w-full">
<div id="container" class="w-full h-[520px]"></div>
<div class="absolute bg-white w-[270px] h-[500px] top-[10px] left-[10px] region-list">
<el-scrollbar>
<div class="p-[10px] region-item pr-[50px] relative"
v-for="(item, index) in formData.area" :key="index"
:class="{ '!border-primary': index == currArea }" @click="selectArea(index)">
<el-form label-width="80px" :model="item" :rules="formRules" class="page-form"
ref="areaFromRef">
<div class="pb-[18px]">
<el-form-item :label="t('areaName')" prop="area_name">
<el-input v-model.trim="formData.area[index].area_name" type="text" />
</el-form-item>
<el-form-item :label="t('deliveryArea')" prop="delivery_area">
<div class="w-full border-[1px] border-solid border-[#dcdfe6] flex" v-loading="mapLoading">
<div class="store-wrap w-[270px] h-[520px] border-r-[1px] border-solid border-[#dcdfe6]">
<div class="relative h-[520px]" v-if="deliveryStoreList.length">
<div class="h-[450px] overflow-y-auto">
<div class="h-[70px] cursor-pointer px-[10px] flex flex flex-col justify-center border-b-[1px] border-solid border-[#dcdfe6] box-border" :class="{'bg-[#E6F1FE] text-primary': curDelivery.store_id == item.store_id }" v-for="(item,index) in deliveryStoreList" :key="index" @click="handleMap(item)">
<div class="text-[16px] leading-[20px] mb-[6px] flex items-center justify-between">
<span class="using-hidden">{{ item.store_name }}</span>
<el-button type="primary" @click.stop="toLink(item)" link class="ml-[10px] !text-[12px]">配置</el-button>
</div>
<div class="pb-[18px]">
<el-form-item :label="t('startPrice')" prop="start_price">
<el-input v-model.trim="formData.area[index].start_price" maxlength="8" type="text"
@keyup="filterDigit($event)" />
<div class="text-[12px] text-[#999] leading-[16px] multi-hidden" :class="{'!text-primary': curDelivery.store_id == item.store_id }">地址{{ item.full_address }}</div>
</div>
</div>
<div class="absolute left-0 right-0 bottom-0 h-[70px] flex items-center bg-[#fff] px-[20px]">
<el-button type="primary" @click="newWindow()" class="w-full box-border">新增配送点</el-button>
</div>
</div>
<div v-else class="h-full flex flex-col items-center justify-center">
<span class="text-[14px] mb-[10px]">请到配送点中添加配送区域</span>
<el-button type="primary" @click="newWindow()" link class="ml-[10px]">{{ t('toSetting') }}</el-button>
</div>
</div>
<div class="relative flex-1">
<div id="container" class="w-full h-[520px]"></div>
<div class="absolute bg-white w-[270px] h-[500px] top-[10px] left-[10px] region-list" v-if="deliveryStoreList.length">
<el-scrollbar>
<div class="p-[10px] region-item pr-[50px] relative" v-for="(item, index) in curDelivery.area" :key="index" :class="{ '!border-primary': index == currArea }" @click="selectArea(index)">
<el-form label-width="80px" :model="item" :rules="formRules" class="page-form" ref="areaFromRef">
<div class="pb-[18px]">
<el-form-item :label="t('areaName')" prop="area_name">
<el-input v-model.trim="curDelivery.area[index].area_name" type="text" @click.stop=""/>
</el-form-item>
</div>
<div class="pb-[18px]">
<el-form-item :label="t('startPrice')" prop="start_price">
<el-input v-model.trim="curDelivery.area[index].start_price" maxlength="8" type="text"
@keyup="filterDigit($event)" @click.stop="" />
</el-form-item>
</div>
<div class="pb-[10px]" v-show="formData.fee_type == 'region'">
<el-form-item :label="t('deliveryPrice')" prop="delivery_price">
<el-input v-model.trim="formData.area[index].delivery_price" type="text"
@keyup="filterDigit($event)" />
<el-input v-model.trim="curDelivery.area[index].delivery_price" type="text"
@keyup="filterDigit($event)" @click.stop=""/>
</el-form-item>
</div>
<el-form-item :label="t('areaType')">
<el-radio-group v-model="formData.area[index].area_type" @change="areaTypeChange(index)">
<el-radio-group v-model="curDelivery.area[index].area_type" @change="areaTypeChange(index)">
<el-radio label="radius" size="large" class="!mr-[10px]">{{ t('radius') }}</el-radio>
<el-radio label="custom" size="large" class="!mr-[0px]">{{ t('custom') }}</el-radio>
</el-radio-group>
@ -225,6 +108,7 @@
<el-button plain @click="addArea">{{ t('addDeliveryArea') }}</el-button>
</div>
</el-scrollbar>
</div>
</div>
</div>
</el-form-item>
@ -240,111 +124,25 @@
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, onBeforeUnmount,toRaw } from 'vue'
import { ref, computed, onMounted, onBeforeUnmount, toRaw } from 'vue'
import { t } from '@/lang'
import { ArrowLeft } from "@element-plus/icons-vue"
import { ArrowLeft } from '@element-plus/icons-vue'
import { useRoute, useRouter } from 'vue-router'
import { getMap } from '@/app/api/sys'
import { guid, filterDigit, deepClone } from '@/utils/common'
import { createCircle, deleteGeometry, createPolygon, selectGeometry, createMarker } from '@/utils/qqmap'
import { setLocal, getLocal ,getThird} from '@/addon/shop/api/delivery'
import { FormInstance } from 'element-plus'
import { setLocal, getLocal, getDeliveryStoreListAll } from '@/addon/shop/api/delivery'
import { FormInstance, ElMessage } from 'element-plus'
import Test from '@/utils/test'
import { getShopDefaultDeliveryAddressInfo } from '@/addon/shop/api/shop_address'
const route = useRoute()
const router = useRouter()
const loading = ref(true)
const pageName = route.meta.title
const formRef = ref<FormInstance>()
const areaFromRef: any = ref<FormInstance[]>()
interface addressType{
full_address:string
lat:string
lng:string
}
const defaultDeliveryAddress:any = ref<addressType|null>(null)
const getDefaultDeliveryAddress = async () => {
await getShopDefaultDeliveryAddressInfo().then(({ data }) => {
defaultDeliveryAddress.value = data
}).catch()
}
getDefaultDeliveryAddress()
const thirdPartyData = ref<any>(null)
const getThirdFn = ()=> {
getThird().then((res) => {
thirdPartyData.value = res.data.map((item: any) => ({
...item,
isEnabled: false, //
config: {
app_key: '',
app_secret: '',
shop_id: '',
shop_store_id: ''
}
}));
})
}
const activeService = computed(() => {
if (!thirdPartyData.value || thirdPartyData.value.length === 0) {
return null;
}
return thirdPartyData.value.find(service => service.isEnabled) || null;
});
//
const handleServiceChange = (index: number) => {
const service = thirdPartyData.value[index];
if (service.isEnabled) {
thirdPartyData.value.forEach((s, i) => {
if (i !== index) {
s.isEnabled = false;
}
});
}
};
const buildThirdPartyConfig = () => {
const config: Record<string, any> = {};
thirdPartyData.value.forEach(service => {
config[service.type] = {
app_key: service.config.app_key,
app_secret: service.config.app_secret,
shop_id: service.config.shop_id,
shop_store_no: service.config.shop_store_no
};
if (service.isEnabled) {
config.default = service.type;
}
});
return config;
};
getThirdFn()
const formData = ref({
center: {
lat: '',
lng: ''
},
// delivery_type: ['business'],
delivery_type: 'business',
fee_type: 'region',
time_is_open:1,
time_type:0,
time_week: <any>[],
base_dist: '',
base_price: '',
grad_dist: '',
grad_price: '',
weight_start: 0.000,
weight_unit: 0,
weight_price: 0,
const deliveryStoreList = ref<any>([])
const curDelivery = ref<any>({
area: [
{
area_name: '',
@ -355,16 +153,27 @@ const formData = ref({
key: guid()
}
}
],
delivery_time: [
{ start_time: '', end_time: '' } //
],
time_interval: 30,
most_day: 7,
advance_day: 0,
time_most: 1,
time_advance:1,
third_party_config:{}
]
})
const getDeliveryStoreListAllFn = async () => {
deliveryStoreList.value = await (await getDeliveryStoreListAll({ pick_up_type: 'local_delivery' })).data
curDelivery.value = deliveryStoreList.value[0]
}
getDeliveryStoreListAllFn()
const formData = ref({
center: {
lat: '',
lng: ''
},
fee_type: 'region',
base_dist: '',
base_price: '',
grad_dist: '',
grad_price: '',
weight_start: 0.000,
weight_unit: 0,
weight_price: 0
})
//
@ -378,39 +187,22 @@ const regExp = {
//
const formRules = computed(() => {
return {
time_week: [{ required: true, message: t('timeWeekRequire'), trigger: 'change' }],
delivery_address: [
{
validator: (rule: any, value: any, callback: any) => {
if (!defaultDeliveryAddress.value) {
callback(new Error(t('defaultDeliveryAddressEmpty')))
}
callback()
}
}
],
// delivery_type: [
// {
// validator: (rule: any, value: any, callback: any) => {
// if (!formData.value.delivery_type.length) {
// callback(new Error(t('deliveryTypeRequire')))
// }
// callback()
// }
// }
// ],
distance: [
{
validator: (rule: any, value: any, callback: any) => {
if (formData.value.fee_type == 'distance') {
if (Test.require(formData.value.base_dist)) {
callback(new Error(t('baseDistRequire')))
} else if (Number(formData.value.base_dist) <= 0) {
callback(new Error(t('起始公里数不能小于等于0')))
}
if (Test.require(formData.value.base_price)) {
callback(new Error(t('basePriceRequire')))
}
if (Test.require(formData.value.grad_dist)) {
callback(new Error(t('gradDistRequire')))
} else if (Number(formData.value.grad_dist) <= 0) {
callback(new Error(t('超出公里数不能小于等于0')))
}
if (Test.require(formData.value.grad_price)) {
callback(new Error(t('gradPriceRequire')))
@ -418,187 +210,32 @@ const formRules = computed(() => {
}
callback()
},
trigger: 'blur'
trigger: ['blur', 'change']
}
],
area_name: [{ required: true, message: t('areaNameRequire'), trigger: 'blur' }],
start_price: [
{ required: true, message: t('startPriceRequire'), trigger: 'blur' },
weight: [
{
validator: (rule: any, value: any, callback: any) => {
if (parseInt(value) < 0) {
callback(new Error(t('startPriceMin')))
}
callback()
},
trigger: 'blur'
}
],
delivery_price: [
{ required: formData.value.fee_type == 'region', message: t('deliveryPriceRequire'), trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
if (parseInt(value) < 0) {
callback(new Error(t('deliveryPriceMin')))
}
callback()
},
trigger: 'blur'
}
],
area: [
{
validator: (rule: any, value: any, callback: any) => {
if (Test.empty(formData.value.area)) {
callback(new Error(t('areaPlaceholder')))
}
callback()
},
trigger: 'blur'
}
],
delivery_time: [
{
validator: (rule: any, value: any, callback: any) => {
if (!value || value.length === 0) {
return callback(new Error(t('tradeTimePlaceholderTwo')))
}
for (let i = 0; i < value.length; i++) {
const timeRange = value[i]
if (!timeRange.start_time || !timeRange.end_time) {
return callback(new Error(t('tradeTimePlaceholderTwo')))
if (formData.value.fee_type == 'distance') {
if (Number(formData.value.weight_start) <= 0) { // 0
callback(new Error(t('商品重量不能小于等于0')))
}
//
if (timeRange.end_time <= timeRange.start_time) {
return callback(new Error(t('tradeTimePlaceholderFour')))
}
//
if (i > 0 && value[i].start_time < value[i - 1].end_time) {
return callback(new Error(t('tradeTimePlaceholderFive')))
if (Number(formData.value.weight_unit) <= 0) { // 0
callback(new Error(t('商品超出重量不能小于等于0')))
}
}
callback()
},
trigger: 'change',
required: true
}
],
time_interval: [
{ required: true, message: t('tradeTimePlaceholderThree'), trigger: 'change' }
],
advance_day:[
{
validator(rule, value, callback) {
if (value === null || value === '') {
callback()
} else if (isNaN(value) || !regExp.number.test(value)) {
callback(t('formatError'))
} else if (value < 0) {
callback(t('notLessThanZero'))
} else {
callback();
}
},
trigger: 'blur'
}
],
most_day:[
{
validator(rule, value, callback) {
if (value === null || value === '') {
callback()
} else if (isNaN(value) || !regExp.number.test(value)) {
callback(t('formatError'))
} else if (value <= 0) {
callback(t('mustBeGreaterThanZero'))
} else {
callback();
}
},
trigger: 'blur'
trigger: ['blur', 'change']
}
]
}
})
const validateThirdPartyConfig = () => {
if (formData.value.delivery_type !== 'third') return true;
const active = thirdPartyData.value.find(item => item.isEnabled);
if (!active) {
ElMessage.error(t('thridRequire'));
return false;
}
const { app_key, app_secret, shop_id ,shop_store_no} = active.config;
if (!app_key || !app_secret || !shop_id || !shop_store_no) {
ElMessage.error(t('thridSeting'));
return false;
}
return true;
};
//
const addTimeRange = () => {
formData.value.delivery_time.push({ start_time: '', end_time: '' })
}
//
const removeTimeRange = (index: number) => {
formData.value.delivery_time.splice(index, 1)
}
const timeTransition = (time:any) => {
const arr = time.split(':')
const num = arr[0] * 60 * 60 + arr[1] * 60
return num
}
const timestampTransition = (timeStamp:any) => {
let hour = Math.floor(timeStamp / (60 * 60))
let minute = Math.floor(timeStamp / 60) - (hour * 60)
hour = hour < 10 ? ('0' + hour) : hour
minute = minute < 10 ? ('0' + minute) : minute
return hour + ':' + minute
}
getLocal().then(({ data }) => {
loading.value = false
if (data) Object.assign(formData.value, data)
formData.value.time_week = formData.value.time_week?formData.value.time_week.split(','):[]
if (Array.isArray(data.delivery_type) && data.delivery_type.length > 0) {
formData.value.delivery_type = data.delivery_type[0];
} else {
formData.value.delivery_type = data.delivery_type || 'business';
}
// delivery_time
if (data.delivery_time === '' || data.delivery_time === null) {
formData.value.delivery_time = [{ start_time: '', end_time: '' }];
} else if (Array.isArray(data.delivery_time)) {
formData.value.delivery_time = data.delivery_time.map((item: any) => ({
start_time: timestampTransition(item.start_time),
end_time: timestampTransition(item.end_time)
}));
}
// third_party_config thirdPartyData
if (data.third_party_config) {
thirdPartyData.value.forEach(service => {
const config = data.third_party_config[service.type];
if (config) {
service.isEnabled = service.type == data.third_party_config.default;
service.config = {
app_key: config.app_key || '',
app_secret: config.app_secret || '',
shop_id: config.shop_id || '',
shop_store_id: config.shop_store_id || '',
shop_store_no: config.shop_store_no || '',
};
}
});
}
}).catch(()=>{
}).catch(() => {
loading.value = false
})
@ -620,34 +257,37 @@ onMounted(() => {
* 初始化地图
*/
let map: any
let marker: any
const mapLoading = ref(true)
const initMap = () => {
const TMap = (window as any).TMap
const LatLng = TMap.LatLng
const center = new LatLng(defaultDeliveryAddress.value ? defaultDeliveryAddress.value.lat : 39.980619, defaultDeliveryAddress.value ? defaultDeliveryAddress.value.lng : 116.321277)
const center = new LatLng(
curDelivery.value ? curDelivery.value.latitude : 39.980619,
curDelivery.value ? curDelivery.value.longitude : 116.321277
)
map = new TMap.Map('container', {
center,
zoom: 14
})
createMarker(map)
marker = createMarker(map)
map.on('tilesloaded', () => {
mapLoading.value = false
})
formData.value.area.forEach(item => {
item.area_type == 'radius' ? createCircle(map, item.area_json) : createPolygon(map, item.area_json)
})
if (curDelivery.value) {
curDelivery.value.area.forEach((item: any) => {
item.area_type == 'radius' ? createCircle(map, item.area_json) : createPolygon(map, item.area_json)
})
}
}
const currArea = ref<number>(0)
/**
* 添加配送区域
*/
const addArea = () => {
formData.value.area.push({
curDelivery.value.area.push({
area_name: '',
area_type: 'radius',
start_price: 0,
@ -656,68 +296,109 @@ const addArea = () => {
key: guid()
}
})
const index = formData.value.area.length - 1
createCircle(map, formData.value.area[index].area_json)
const index = curDelivery.value.area.length - 1
createCircle(map, curDelivery.value.area[index].area_json)
}
/**
* 删除配送区域
*/
const deleteArea = (index: number) => {
const data = formData.value.area[index]
const data = curDelivery.value.area[index]
deleteGeometry(data.area_json.key)
formData.value.area.splice(index, 1)
curDelivery.value.area.splice(index, 1)
}
const selectArea = (index: number) => {
currArea.value = index
const data = formData.value.area[index]
const data = curDelivery.value.area[index]
selectGeometry(data.area_json.key)
}
const areaTypeChange = (index: number) => {
const data = formData.value.area[index]
const data = curDelivery.value.area[index]
deleteGeometry(data.area_json.key)
data.area_type == 'radius' ? createCircle(map, data.area_json) : createPolygon(map, data.area_json)
}
const handleMap = (data:any) => {
deliveryStoreList.value.forEach((item: any) => {
if (item.store_id == curDelivery.value.store_id) {
item = deepClone(toRaw(item))
}
})
curDelivery.value.area.forEach((item: any) => {
deleteGeometry(item.area_json.key)
})
curDelivery.value = data
setTimeout(() => {
const latLng = new (window as any).TMap.LatLng(curDelivery.value.latitude, curDelivery.value.longitude)
map.setCenter(latLng)
marker.updateGeometries({
id: 'center',
position: latLng
})
curDelivery.value.area.forEach((item: any) => {
//
item.area_json.center = {
lat: curDelivery.value.latitude,
lng: curDelivery.value.longitude
}
item.area_type == 'radius' ? createCircle(map, item.area_json) : createPolygon(map, item.area_json)
})
}, 500)
}
onBeforeUnmount(() => {
map.destroy()
map?.destroy()
})
const verify = () => {
let flag = true
for (let i = 0; i < deliveryStoreList.value.length; i++) {
const temp = deliveryStoreList.value[i].area
if (Test.require(temp)) {
flag = false
ElMessage({ type: 'warning', message: `门店${deliveryStoreList.value[i].store_name}的配送区域不能为空` })
break
}
for (let j = 0; j < temp.length; j++) {
const val = temp[j]
if (Test.require(val.area_name)) {
flag = false
ElMessage({ type: 'warning', message: `门店${deliveryStoreList.value[i].store_name}${j + 1}的配送区域名称不能为空` })
break
}
if (parseInt(val.start_price) < 0) {
flag = false
ElMessage({ type: 'warning', message: `门店${deliveryStoreList.value[i].store_name}${j + 1}的起送价不能小于0` })
break
}
if (parseInt(val.delivery_price) < 0 && formData.value.fee_type == 'region') {
flag = false
ElMessage({ type: 'warning', message: `门店${deliveryStoreList.value[i].store_name}${j + 1}的配送费不能小于0` })
break
}
}
if (!flag) break
}
return flag
}
const onSave = async (formEl: FormInstance | undefined) => {
if (!verify()) return
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
let areaValidate = true
for (let i = 0; i < areaFromRef.value?.length; i++) {
const ref = areaFromRef.value[i]
await ref.validate(async (valid) => {
areaValidate = valid
})
if (!areaValidate) break
}
if (!areaValidate) return
if (!validateThirdPartyConfig()) return;
if (valid) {
loading.value = true
formData.value.center = {
lat: defaultDeliveryAddress.value.lat,
lng: defaultDeliveryAddress.value.lng
}
await formEl.validate(async (valid) => {
const param = deepClone(toRaw(formData.value))
param.time_week = param.time_week.toString()
param.delivery_time = param.delivery_time.map(range => ({
start_time: range.start_time ? timeTransition(range.start_time) : null,
end_time: range.end_time ? timeTransition(range.end_time) : null
}))
param.third_party_config = buildThirdPartyConfig()
param.delivery_type = [param.delivery_type]
const data: any = {}
deliveryStoreList.value.forEach((item: any) => {
item = deepClone(toRaw(item))
data[item.store_id] = item.area
})
param.area_data = data
setLocal(param).then(() => {
loading.value = false
}).catch(() => {
@ -727,9 +408,25 @@ const onSave = async (formEl: FormInstance | undefined) => {
}
})
}
//
const newWindow = () => {
const url = router.resolve({
path: '/shop/delivery_store'
})
window.open(url.href)
}
const back = () => {
router.push({ path: '/shop/order/delivery' })
router.push({ path: '/shop/delivery/config' })
}
const toLink = (data: any) => {
const url = router.resolve({
path: '/shop/delivery_store/edit',
query: {
store_id: data.store_id
}
})
window.open(url.href)
}
</script>
@ -746,4 +443,14 @@ const back = () => {
#container :deep(div){
z-index: 2 !important;
}
.store-wrap::-webkit-scrollbar{
width:4px;
border-radius:2px;
background-color:#f1f1f1;
}
.store-wrap::-webkit-scrollbar-thumb{
background-color:#c1c1c1;
border-radius:2px;
}
</style>

View File

@ -0,0 +1,80 @@
<template>
<div class="main-container bg-[#fff] rounded-[4px]">
<div class="flex ml-[18px] justify-between items-center pt-[20px]">
<span class="text-page-title">{{ pageName }}</span>
</div>
<el-form :model="formData" label-width="150px" ref="formRef" class="page-form" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<el-form-item :label="t('isStoreSelect')">
<div>
<el-switch v-model="formData.is_local_delivery_store_select" :active-value="1" :inactive-value="0" />
<div class="text-[12px] text-[#999]">不开启时将默认选择就近配送门店开启后同城配送下单时可主动选择配送门店</div>
</div>
</el-form-item>
<el-form-item :label="t('isShowPolyline')">
<div>
<el-switch v-model="formData.is_show_polyline" :active-value="1" :inactive-value="0" />
<div class="text-[12px] text-[#999]">此功能需耗费配额,需要具备"驾车路线规划"接口的调用权限和配额方可使用</div>
</div>
</el-form-item>
</el-card>
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="loading" @click="save(formRef)">{{ t('save') }}</el-button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { setDeliveryConfig, getDeliveryConfig } from '@/addon/shop/api/delivery'
import { FormInstance } from 'element-plus'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title
const loading = ref(true)
const formData = reactive<any>({
is_local_delivery_store_select: 1,
is_show_polyline: 1
})
const formRef = ref<FormInstance>()
/**
* 获取配置
*/
getDeliveryConfig().then(res => {
Object.assign(formData, res.data)
loading.value = false
}).catch(() => {
loading.value = false
})
/**
* 保存
*/
const save = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
setDeliveryConfig(formData).then(() => {
loading.value = false
}).catch(() => {
loading.value = false
})
}
})
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,93 @@
<template>
<!--三方配送设置-->
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
</div>
<div class="mt-[20px]">
<el-table :data="thirdDeliveryTableData.data" size="large" v-loading="thirdDeliveryTableData.loading">
<template #empty>
<span>{{ !thirdDeliveryTableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="name" :label="t('name')" min-width="100" :show-overflow-tooltip="true"/>
<el-table-column :label="t('isUse')" min-width="180" align="center">
<template #default="{ row }">
<el-tag class="ml-2" :type="row.is_use == 1 ? 'success': 'error'">{{ row.is_use == 1 ? t('statusNormal') : t('statusDeactivate')}}</el-tag>
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" fixed="right" min-width="200">
<template #default="{ row, $index }">
<template v-if="row.is_merchant">
<el-button type="primary" link @click="toStaff">配送员</el-button>
<el-button type="primary" link @click="toRecord">配送记录</el-button>
</template>
<el-button type="primary" link @click="editEvent(row, $index)" v-if="!row.is_merchant">{{ t('config') }}</el-button>
</template>
</el-table-column>
</el-table>
</div>
<template v-for="(item, index) in thirdDeliveryTableData.data">
<component :is="item.component" :ref="(el) => setThirdDeliveryTypeRefs(el, index)" v-if="item.component" @complete="loadThirdDeliveryList()"/>
</template>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { defineAsyncComponent, reactive, ref } from 'vue'
import { t } from '@/lang'
import { getLocalDeliveryServiceList } from '@/addon/shop/api/delivery'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const thirdDeliveryTypeRefs = ref([])
const thirdDeliveryTableData = reactive({
loading: true,
data: []
})
const modules: any = import.meta.glob('@/addon/**/views/delivery/components/*.vue')
/**
* 获取配置信息
*/
const loadThirdDeliveryList = () => {
thirdDeliveryTableData.loading = true
getLocalDeliveryServiceList().then(({ data }) => {
Object.keys(data).forEach((key: string) => {
data[key].component && (data[key].component = defineAsyncComponent(modules[data[key].component]))
})
thirdDeliveryTableData.data = data
thirdDeliveryTableData.loading = false
}).catch(() => {
thirdDeliveryTableData.loading = false
})
}
const setThirdDeliveryTypeRefs = (el, index) => {
thirdDeliveryTypeRefs.value[index] = (el)
}
loadThirdDeliveryList()
const editEvent = (data: any, index: number) => {
thirdDeliveryTypeRefs.value[index].setFormData(data)
thirdDeliveryTypeRefs.value[index].showDialog = true
}
const toStaff = () => {
router.push('/shop/delivery/staff')
}
const toRecord = () => {
router.push('/shop/delivery/merchant/record')
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,287 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="localRecordTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('deliveryNo')" prop="delivery_no">
<el-input v-model.trim="localRecordTable.searchParam.delivery_no" :placeholder="t('deliveryNoPlaceholder')" />
</el-form-item>
<el-form-item :label="t('tradeNo')" prop="trade_no">
<el-input v-model.trim="localRecordTable.searchParam.trade_no" :placeholder="t('tradeNoPlaceholder')" />
</el-form-item>
<el-form-item :label="t('deliveryStatus')" prop="status">
<el-select v-model="localRecordTable.searchParam.status" :placeholder="t('deliveryStatusPlaceholder')" clearable>
<el-option v-for="(item,key) in statusList" :key="key" :label="item" :value="key" />
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker v-model="localRecordTable.searchParam.create_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadLocalDeliveryList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table :data="localRecordTable.data" size="large" v-loading="localRecordTable.loading">
<template #empty>
<span>{{ !localRecordTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="delivery_service_name" :label="t('deliveryServiceName')" min-width="120" />
<el-table-column prop="body" :label="t('goodsInfo')" min-width="120" />
<el-table-column prop="delivery_no" :label="t('deliveryNo')" min-width="120" />
<el-table-column prop="trade_no" :label="t('tradeNo')" min-width="120" />
<el-table-column prop="delivery_start" :label="t('deliveryStart')" min-width="120">
<template #default="{ row }">
<div class="cursor-pointer" @click="openMap(row,'start')">
<span>{{ row.delivery_start }}</span>
<span class="text-primary ml-[4px] nc-iconfont nc-icon-chakandituV6xx" ></span>
</div>
</template>
</el-table-column>
<el-table-column prop="delivery_end" :label="t('deliveryEnd')" min-width="120">
<template #default="{ row }">
<div class="cursor-pointer" @click="openMap(row, 'end')">
<span>{{ row.delivery_end }}</span>
<span class="text-primary ml-[4px] nc-iconfont nc-icon-chakandituV6xx" ></span>
</div>
</template>
</el-table-column>
<el-table-column prop="status_name" :label="t('deliveryStatus')" min-width="120" />
<el-table-column prop="" :label="t('deliveryDistance')" min-width="120">
<template #default="{ row }">
{{ distance(row.delivery_distance) }}
</template>
</el-table-column>
<el-table-column :label="t('deliveryMoney')" min-width="120">
<template #default="{ row }">
{{ row.delivery_money }}
</template>
</el-table-column>
<el-table-column prop="create_time" :label="t('createTime')" min-width="120" />
<el-table-column prop="remark" :label="t('remark')" min-width="120" />
<el-table-column :label="t('operation')" fixed="right" min-width="120" align="right">
<template #default="{ row }">
<el-button type="primary" link @click="detailEvent(row)">{{ t('detail') }}</el-button>
<el-button type="primary" link @click="syncEvent(row)" v-if="row.pre_status_name?.sync && row.pre_status_name?.sync.indexOf(Number(row.status)) != -1">{{ t('sync') }}</el-button>
<el-button type="primary" link @click="finishEvent(row)" v-if="row.pre_status_name?.finish && row.pre_status_name?.finish.indexOf(Number(row.status)) != -1">{{ t('finish') }}</el-button>
<el-button type="primary" link @click="cancelEvent(row)" v-if="row.pre_status_name?.cancel && row.pre_status_name?.cancel.indexOf(Number(row.status)) != -1">{{ t('cancel') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="localRecordTable.page" v-model:page-size="localRecordTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="localRecordTable.total"
@size-change="loadLocalDeliveryList()" @current-change="loadLocalDeliveryList" />
</div>
</div>
</el-card>
<local-record-detail ref="localRecordDetailRef" />
<!-- 查看地图 -->
<map-info ref="mapInfoRef" />
<el-dialog v-model="showDialog" :title="t('deliveryCancel')" width="480" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<template v-if="formData.cancel_type != 'merchant'">
<el-form-item :label="t('cancelReason')" prop="cancel_reason_id">
<el-select v-model="formData.cancel_reason_id" clearable class="input-item" :placeholder="t('cancelReasonSelectPlaceholder')">
<el-option v-for="(item, index) in reasonList" :key="index" :label="item" :value="index"></el-option>
</el-select>
</el-form-item>
<el-form-item prop="cancel_reason" v-if="formData.cancel_reason_id == 10000">
<el-input v-model="formData.cancel_reason" type="textarea" rows="4" maxlength="200" clearable class="input-width" :placeholder="t('cancelReasonPlaceholder')" />
</el-form-item>
</template>
<el-form-item :label="t('cancelReason')" prop="cancel_reason" v-if="formData.cancel_type == 'merchant'">
<el-input v-model="formData.cancel_reason" type="textarea" rows="4" maxlength="200" clearable class="input-width" :placeholder="t('cancelReasonPlaceholder')" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ElMessageBox, FormInstance } from 'element-plus'
import { t } from '@/lang'
import { distance } from '@/utils/common'
import { computed, reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { getLocalDeliveryList, getLocalDeliveryStatus, getLocalDeliverySync, getLocalDeliveryCancelReason, getDeliveryFinish, setLocalDeliveryCancel } from '@/addon/shop/api/delivery'
import LocalRecordDetail from '@/addon/shop/views/delivery/components/local_record_detail.vue'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const localRecordTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
delivery_no: '',
trade_no: route.query.trade_no || '',
trade_type: route.query.trade_type || '',
status: '',
create_time: ''
}
})
const statusList = ref<any>([])
const searchFormRef = ref<FormInstance>()
const getLocalDeliveryStatusFn = () => {
getLocalDeliveryStatus().then(res => {
statusList.value = res.data
})
}
getLocalDeliveryStatusFn()
/**
* 获取配送记录列表
*/
const loadLocalDeliveryList = (page: number = 1) => {
localRecordTable.loading = true
localRecordTable.page = page
getLocalDeliveryList({
page: localRecordTable.page,
limit: localRecordTable.limit,
...localRecordTable.searchParam
}).then((res: any) => {
localRecordTable.loading = false
localRecordTable.data = res.data.data
localRecordTable.total = res.data.total
}).catch(() => {
localRecordTable.loading = false
})
}
loadLocalDeliveryList()
//
const localRecordDetailRef = ref()
const detailEvent = (row: any) => {
localRecordDetailRef.value?.setFormData(row)
}
//
const mapInfoRef = ref()
const openMap = (data: any, type: string) => {
const obj: any = {}
if (type == 'start') {
obj.taker_latitude = data.delivery_start_lat
obj.taker_longitude = data.delivery_start_lng
obj.taker_address = data.delivery_start
} else {
obj.taker_latitude = data.delivery_end_lat
obj.taker_longitude = data.delivery_start_lng
obj.taker_address = data.delivery_end
}
mapInfoRef.value?.setFormData(obj)
}
//
const syncEvent = (data: any) => {
ElMessageBox.confirm(t('syncTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
getLocalDeliverySync(data.id).then(() => {
loadLocalDeliveryList()
})
})
}
//
const finishEvent = (data: any) => {
ElMessageBox.confirm(t('finishTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
getDeliveryFinish(data.id).then(() => {
loadLocalDeliveryList()
})
})
}
//
const showDialog = ref(false)
const reasonList = ref([])
const loading = ref(false)
const formData = reactive<any>({
id: '',
cancel_reason_id: '',
cancel_reason: '',
cancel_type: ''
})
const formRef = ref<FormInstance>()
const cancelEvent = async (data: any) => {
formData.id = data.id
formData.cancel_type = data.delivery_service
if (data.delivery_service != 'merchant') {
reasonList.value = await (await getLocalDeliveryCancelReason({ service: data.delivery_service })).data
}
showDialog.value = true
}
//
const formRules = computed(() => {
return {
cancel_reason_id: [
{ required: formData.cancel_type != 'merchant', message: t('cancelReasonSelectPlaceholder'), trigger: 'blur' }
],
cancel_reason: [
{ required: formData.cancel_reason_id == 10000 || formData.cancel_type == 'merchant', message: t('cancelReasonPlaceholder'), trigger: 'blur' }
]
}
})
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
setLocalDeliveryCancel(formData).then(res => {
loading.value = false
showDialog.value = false
setTimeout(() => {
loadLocalDeliveryList()
}, 1500)
}).catch(() => {
loading.value = false
})
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadLocalDeliveryList()
}
</script>
<style scoped lang="scss">
:deep(.el-form-item){
align-items: flex-start;
}
</style>

View File

@ -0,0 +1,249 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="shopRecordTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('deliveryNo')" prop="delivery_no">
<el-input v-model.trim="shopRecordTable.searchParam.delivery_no" :placeholder="t('deliveryNoPlaceholder')" />
</el-form-item>
<el-form-item :label="t('tradeNo')" prop="trade_no">
<el-input v-model.trim="shopRecordTable.searchParam.trade_no" :placeholder="t('tradeNoPlaceholder')" />
</el-form-item>
<el-form-item :label="t('deliveryStatus')" prop="status">
<el-select v-model="shopRecordTable.searchParam.status" :placeholder="t('deliveryStatusPlaceholder')" clearable>
<el-option v-for="(item,key) in statusList" :key="key" :label="item" :value="key" />
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker v-model="shopRecordTable.searchParam.create_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadShopDeliveryList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table :data="shopRecordTable.data" size="large" v-loading="shopRecordTable.loading">
<template #empty>
<span>{{ !shopRecordTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="delivery_no" :label="t('deliveryNo')" min-width="120" />
<el-table-column prop="body" :label="t('goodsInfo')" min-width="120" />
<el-table-column prop="trade_no" :label="t('tradeNo')" min-width="120" />
<el-table-column prop="delivery_start" :label="t('deliveryStart')" min-width="120">
<template #default="{ row }">
<div class="cursor-pointer" @click="openMap(row,'start')">
<span>{{ row.delivery_start }}</span>
<span class="text-primary ml-[4px] nc-iconfont nc-icon-chakandituV6xx"></span>
</div>
</template>
</el-table-column>
<el-table-column prop="delivery_end" :label="t('deliveryEnd')" min-width="120">
<template #default="{ row }">
<div class="cursor-pointer" @click="openMap(row, 'end')">
<span>{{ row.delivery_end }}</span>
<span class="text-primary ml-[4px] cursor-pointer nc-iconfont nc-icon-chakandituV6xx"></span>
</div>
</template>
</el-table-column>
<el-table-column prop="status_name" :label="t('deliveryStatus')" min-width="120" />
<el-table-column prop="delivery_distance" :label="t('deliveryDistance')" min-width="120">
<template #default="{ row }">
{{ distance(row.delivery_distance) }}
</template>
</el-table-column>
<el-table-column prop="delivery_money" :label="t('deliveryMoney')" min-width="120">
<template #default="{ row }">
{{ row.delivery_money }}
</template>
</el-table-column>
<el-table-column prop="create_time" :label="t('createTime')" min-width="120" />
<el-table-column prop="remark" :label="t('remark')" min-width="120" />
<el-table-column :label="t('operation')" fixed="right" min-width="120" align="right">
<template #default="{ row }">
<el-button type="primary" link @click="detailEvent(row)">{{ t('detail') }}</el-button>
<el-button type="primary" link @click="finishEvent(row)" v-if="row.pre_status_name?.finish && row.pre_status_name?.finish.indexOf(Number(row.status)) != -1">{{ t('finish') }}</el-button>
<el-button type="primary" link @click="cancelEvent(row)" v-if="row.pre_status_name?.cancel && row.pre_status_name?.cancel.indexOf(Number(row.status)) != -1">{{ t('cancel') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="shopRecordTable.page" v-model:page-size="shopRecordTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="shopRecordTable.total"
@size-change="loadShopDeliveryList()" @current-change="loadShopDeliveryList" />
</div>
</div>
</el-card>
<shop-record-detail ref="shopRecordDetailRef" />
<!-- 查看地图 -->
<map-info ref="mapInfoRef" />
<el-dialog v-model="showDialog" :title="t('deliveryCancel')" width="480" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('cancelReason')" prop="cancel_reason">
<el-input v-model="formData.cancel_reason" type="textarea" rows="4" maxlength="200" clearable class="input-width" :placeholder="t('cancelReasonPlaceholder')" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ElMessageBox, FormInstance } from 'element-plus'
import { t } from '@/lang'
import { distance } from '@/utils/common'
import { computed, reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { getShopDeliveryOrderList, getShopDeliveryStatus, getShopDeliveryFinish, setShopDeliveryCancel } from '@/addon/shop/api/delivery'
import ShopRecordDetail from '@/addon/shop/views/delivery/components/shop_record_detail.vue'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const shopRecordTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
delivery_no: '',
trade_no: route.query.trade_no || '',
trade_type: route.query.trade_type || '',
status: '',
create_time: ''
}
})
const statusList = ref<any>([])
const searchFormRef = ref<FormInstance>()
const getShopDeliveryStatusFn = () => {
getShopDeliveryStatus().then(res => {
statusList.value = res.data
})
}
getShopDeliveryStatusFn()
/**
* 获取配送记录列表
*/
const loadShopDeliveryList = (page: number = 1) => {
shopRecordTable.loading = true
shopRecordTable.page = page
getShopDeliveryOrderList({
page: shopRecordTable.page,
limit: shopRecordTable.limit,
...shopRecordTable.searchParam
}).then((res: any) => {
shopRecordTable.loading = false
shopRecordTable.data = res.data.data
shopRecordTable.total = res.data.total
}).catch(() => {
shopRecordTable.loading = false
})
}
loadShopDeliveryList()
//
const shopRecordDetailRef = ref()
const detailEvent = (row: any) => {
shopRecordDetailRef.value?.setFormData(row)
}
//
const mapInfoRef = ref()
const openMap = (data: any, type: string) => {
const obj: any = {}
if (type == 'start') {
obj.taker_latitude = data.delivery_start_lat
obj.taker_longitude = data.delivery_start_lng
obj.taker_address = data.delivery_start
} else {
obj.taker_latitude = data.delivery_end_lat
obj.taker_longitude = data.delivery_start_lng
obj.taker_address = data.delivery_end
}
mapInfoRef.value?.setFormData(obj)
}
//
const finishEvent = (data: any) => {
ElMessageBox.confirm(t('finishTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
getShopDeliveryFinish(data.id).then(() => {
loadShopDeliveryList()
})
})
}
//
const showDialog = ref(false)
const loading = ref(false)
const formData = reactive<any>({
id: '',
cancel_reason: ''
})
const formRef = ref<FormInstance>()
const cancelEvent = async (data: any) => {
formData.id = data.id
showDialog.value = true
}
//
const formRules = computed(() => {
return {
cancel_reason: [
{ required: true, message: t('cancelReasonPlaceholder'), trigger: 'blur' }
]
}
})
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
setShopDeliveryCancel(formData).then(res => {
loading.value = false
showDialog.value = false
loadShopDeliveryList()
}).catch(() => {
loading.value = false
})
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadShopDeliveryList()
}
</script>
<style scoped lang="scss">
:deep(.el-form-item){
align-items: flex-start;
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push({ path: '/shop/order/delivery' })" />
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push({ path: '/shop/delivery/config' })" />
</el-card>
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">

View File

@ -1,7 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push('/shop/order/delivery')" />
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push('/shop/delivery/config')" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
@ -104,7 +104,7 @@ loadTemplateList(getTablePageStorage(templateTable.searchParam).page);
* 添加运费模板
*/
const addEvent = () => {
router.push({ path: '/shop/order/shipping/template_edit' })
router.push({ path: '/shop/shipping/template_edit' })
}
/**
@ -112,7 +112,7 @@ const addEvent = () => {
* @param data
*/
const editEvent = (data: any) => {
router.push({ path: '/shop/order/shipping/template_edit', query: { id: data.template_id } })
router.push({ path: '/shop/shipping/template_edit', query: { id: data.template_id } })
}
/**

View File

@ -474,7 +474,7 @@ const onSave = async (formEl: FormInstance | undefined) => {
data.area = Object.values(area)
save(data).then(() => {
loading.value = false
router.push({ path: '/shop/order/shipping/template' })
router.push({ path: '/shop/shipping/template' })
}).catch(() => {
loading.value = false
})
@ -483,7 +483,7 @@ const onSave = async (formEl: FormInstance | undefined) => {
}
const back = () => {
router.push({ path: '/shop/order/shipping/template' })
router.push({ path: '/shop/shipping/template' })
}
</script>

View File

@ -0,0 +1,164 @@
<template>
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push(`/shop/delivery_store`)" />
</el-card>
<el-card class="box-card !border-none" shadow="never">
<el-table :data="deliveryServiceTable.data" size="large" v-loading="deliveryServiceTable.loading">
<template #empty>
<span>{{ !deliveryServiceTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="name" :label="t('deliveryServiceName')" min-width="200" :show-overflow-tooltip="true"/>
<el-table-column prop="open_status" :label="t('openStatus')" width="200">
<template #default="{ row }">
<div v-if="row.open_status == ''"></div>
<el-tag v-else class="cursor-pointer" :type="row.open_status == 1 ? 'success' : 'danger'">{{ row.open_status == 1 ? t('pass') : t('notPass') }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="edit_status" :label="t('editStatus')" width="200">
<template #default="{ row }">
<div v-if="row.edit_status == ''"></div>
<el-tag v-else class="cursor-pointer" :type="row.edit_status == 1 ? 'success' : 'danger'">{{ row.edit_status == 1 ? t('pass') : t('notPass') }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="reason" :label="t('reason')" :show-overflow-tooltip="true" min-width="500"/>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="openEvent(row)" v-if="row.is_merchant == 0 && row.open_status != 1">{{ t('openService') }}</el-button>
<el-button type="primary" link @click="editEvent(row)" v-if="row.is_merchant == 0 && row.open_status == 1">{{ t('editService') }}</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 开通/修改品类 -->
<el-dialog v-model="showDialog" :title="title" width="500px" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRulesDeliverySet" class="page-form" v-loading="loadingDeliverySet">
<el-form-item :label="t('business')" prop="business">
<el-select v-model="formData.business" clearable :placeholder="t('businessPlaceholder')">
<el-option :label="value" :value="key" v-for="(value, key, index) in businessOptions" :key="index"/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loadingDeliverySet" @click="confirmDeliverySet(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, computed } from 'vue'
import { t } from '@/lang'
import { ArrowLeft } from '@element-plus/icons-vue'
import { FormInstance } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { getDeliveryServiceList, deliveryShopOpen, deliveryShopEdit } from '@/addon/shop/api/delivery'
import { cloneDeep } from 'lodash-es'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const id:any = ref(route.query.id || 0)
const deliveryServiceTable = reactive({
loading: true,
data: []
})
const loadDeliveryServiceList = () => {
//
getDeliveryServiceList(id.value).then((res: any) => {
deliveryServiceTable.data = cloneDeep(res.data)
deliveryServiceTable.loading = false
})
}
loadDeliveryServiceList()
const businessOptions = ref({})
const showDialog = ref(false)
const loadingDeliverySet = ref(false)
const formRef = ref<FormInstance>()
const formData = reactive({
id: id.value,
delivery_type: '',
business: '',
extend_data: {},
})
//
const formRulesDeliverySet = computed(() => {
return {
business: [
{ required: true, message: t('businessPlaceholder'), trigger: 'blur' }
]
}
})
const title = ref('')
const isOpen = ref(false)
//
const openEvent = (data:any) => {
title.value = t('openService') + data.name
businessOptions.value = data.business_list
formData.delivery_type = data.key
formData.extend_data = data.extend_data
showDialog.value = true
}
//
const editEvent = (data:any) => {
title.value = t('editService')
businessOptions.value = data.business_list
formData.delivery_type = data.key
formData.business = data.business
showDialog.value = true
isOpen.value = data.open_status == 1
}
/**
* 确认
* @param formEl
*/
const confirmDeliverySet = async (formEl: FormInstance | undefined) => {
if (loadingDeliverySet.value || !formEl) return
const save = isOpen.value ? deliveryShopEdit : deliveryShopOpen
await formEl.validate(async (valid) => {
if (valid) {
loadingDeliverySet.value = true
save(formData).then(res => {
loadingDeliverySet.value = false
showDialog.value = false
loadDeliveryServiceList()
}).catch(() => {
loadingDeliverySet.value = false
})
}
})
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,898 @@
<template>
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back" />
</el-card>
<el-card class="box-card !border-none" shadow="never">
<el-alert type="warning" :closable="false" class="!mb-[15px]" v-if="formData.is_system === 1">
<template #default>
<p>{{ t('deliveryStoreTips') }}<span class="text-primary cursor-pointer" @click="newWindow()">去修改</span><span class="text-primary cursor-pointer ml-[10px]" @click="refreshAddress(true)">刷新</span></p>
</template>
</el-alert>
<el-form label-width="120px" ref="formRef" :rules="formRules" :model="formData" class="page-form" v-loading="loading">
<el-form-item :label="t('提货类型')" prop="pick_up_type">
<el-checkbox-group v-model="formData.pick_up_type" @change="changePickUpType">
<el-checkbox :label="index" v-for="(item,index) in pickUpType" :key="index">{{ item }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item :label="t('storeName')" prop="store_name">
<el-input v-model.trim="formData.store_name" clearable :placeholder="t('storeNamePlaceholder')" class="input-width" maxlength="30" :disabled="formData.is_system === 1" />
</el-form-item>
<el-form-item :label="t('contactName')" prop="contact_name">
<el-input v-model.trim="formData.contact_name" clearable :placeholder="t('contactNamePlaceholder')" class="input-width" maxlength="30" :disabled="formData.is_system === 1" />
</el-form-item>
<el-form-item :label="t('storeMobile')" prop="store_mobile">
<el-input v-model.trim="formData.store_mobile" clearable :placeholder="t('storeMobilePlaceholder')" class="input-width" @keyup="filterNumber($event)" @blur="formData.store_mobile = $event.target.value" maxlength="11" :disabled="formData.is_system === 1" />
</el-form-item>
<el-form-item :label="t('tradeTime')" prop="trade_time">
<div>
<el-input v-model.trim="formData.trade_time" clearable :placeholder="t('tradeTimePlaceholder')" class="input-width" :disabled="formData.is_system === 1" />
<p class="text-[12px] text-[#999]">{{ t('tradeTimeTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('开启状态')" prop="status">
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item :label="t('timeIsOpen')" prop="time_is_open">
<div>
<el-radio-group v-model="formData.time_is_open">
<el-radio :label="1">{{ t('open') }}</el-radio>
<el-radio :label="0">{{ t('close') }}</el-radio>
</el-radio-group>
<div class="mt-[10px] text-[12px] text-[#999] leading-[20px]">{{t('timeIsOpenTips')}}</div>
</div>
</el-form-item>
<template v-if="formData.time_is_open === 1">
<el-form-item :label="t('自提日期')" prop="time_week" >
<el-checkbox-group v-model="formData.time_week">
<el-checkbox :label="'1'">{{ t('monday') }}</el-checkbox>
<el-checkbox :label="'2'">{{ t('tuesday') }}</el-checkbox>
<el-checkbox :label="'3'">{{ t('wednesday') }}</el-checkbox>
<el-checkbox :label="'4'">{{ t('thursday') }}</el-checkbox>
<el-checkbox :label="'5'">{{ t('friday') }}</el-checkbox>
<el-checkbox :label="'6'">{{ t('saturday') }}</el-checkbox>
<el-checkbox :label="'0'">{{ t('sunday') }}</el-checkbox>
<br />
</el-checkbox-group>
</el-form-item>
<el-form-item :label="t('deliveryTime')" prop="trade_time_json">
<div>
<div>
<div v-for="(timeRange, index) in formData.trade_time_json" :key="index" class="mb-3">
<el-time-picker v-model="timeRange.start_time" :placeholder="t('startTime')"
format="HH:mm" value-format="HH:mm"
:picker-options="{selectableRange: '00:00 - 23:59'}" />
<span class="mx-2">-</span>
<el-time-picker v-model="timeRange.end_time" :placeholder="t('endTime')" format="HH:mm"
value-format="HH:mm" :picker-options="{selectableRange: '00:00 - 23:59'}" />
<span v-if="index > 0" class="text-primary cursor-pointer ml-[10px]"
@click="removeTimeRange(index)"> {{ t('delete') }}</span>
</div>
<span class="text-primary cursor-pointer mr-[10px]" @click="addTimeRange"
v-if="formData.trade_time_json.length < 3"> {{ t('addTime') }}</span>
</div>
<div class="text-[12px] text-[#999]">{{ t('deliveryTimeTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('timeInterval')" prop="time_interval">
<div>
<el-radio-group v-model="formData.time_interval">
<el-radio v-for="(item, key) in time_interval_list" :key="key" :label="item.type">{{ item.name }}</el-radio>
</el-radio-group>
</div>
</el-form-item>
</template>
<template v-if="formData.pick_up_type.indexOf('local_delivery') != -1">
<el-form-item :label="t('feeType')">
<el-radio-group v-model="initData.fee_type" >
<el-radio label="region" disabled>{{ t('region') }}</el-radio>
<el-radio label="distance" disabled>{{ t('distance') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('feeSetting')" prop="distance" v-show="initData.fee_type == 'distance'">
<div class="flex">
<div class="w-[60px] mx-[5px]">
<el-input v-model.number="initData.base_dist" disabled type="text" maxlength="6" @keyup="filterDigit($event)" />
</div>
{{ t('feeSettingTextOne') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.trim="initData.base_price" disabled type="text" maxlength="8" @keyup="filterDigit($event)" />
</div>
{{ t('feeSettingTextTwo') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.number="initData.grad_dist" disabled type="text" maxlength="6" @keyup="filterDigit($event)" />
</div>
{{ t('feeSettingTextThree') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.trim="initData.grad_price" disabled type="text" maxlength="8" @keyup="filterDigit($event)" />
</div>
{{ t('priceUnit') }}
</div>
</el-form-item>
<el-form-item :label="t('weightFee')" prop="weight">
<div class="flex">
{{ t('weightFeeTextOne') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.trim="initData.weight_start" disabled type="text" maxlength="6" @keyup="filterDigit($event)" />
</div>
{{ t('weightFeeTextTwo') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.trim="initData.weight_unit" disabled type="text" maxlength="6" @keyup="filterDigit($event)" />
</div>
{{ t('weightFeeTextThree') }}
<div class="w-[60px] mx-[5px]">
<el-input v-model.trim="initData.weight_price" disabled type="text" maxlength="8" @keyup="filterDigit($event)" />
</div>
{{ t('priceUnit') }}
</div>
</el-form-item>
</template>
<el-form-item :label="t('地址')" prop="address_area">
<el-select v-model="formData.province_id" value-key="id" @change="handleFlash" clearable class="w-[200px]" ref="provinceRef" :disabled="formData.is_system === 1">
<el-option :label="t('provincePlaceholder')" :value="0"/>
<el-option v-for="(item, index) in areaList.province" :key="index" :label="item.name" :value="item.id"/>
</el-select>
<el-select v-model="formData.city_id" value-key="id" @change="handleFlash" clearable class="w-[200px] ml-3" ref="cityRef" :disabled="formData.is_system === 1">
<el-option :label="t('cityPlaceholder')" :value="0"/>
<el-option v-for="(item, index) in areaList.city " :key="index" :label="item.name" :value="item.id"/>
</el-select>
<el-select v-model="formData.district_id" value-key="id" @change="handleFlash" clearable class="w-[200px] ml-3" ref="districtRef" :disabled="formData.is_system === 1">
<el-option :label="t('districtPlaceholder')" :value="0"/>
<el-option v-for="(item, index) in areaList.district " :key="index" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item prop="address">
<div>
<el-input v-model.trim="formData.address" clearable :placeholder="t('addressPlaceholder')" @input="areaChange()" class="input-width" :disabled="formData.is_system === 1"/>
</div>
</el-form-item>
<el-form-item prop="area" v-loading="mapLoading">
<div class="relative w-full">
<div id="container" class="w-full h-[520px]"></div>
<div class="absolute bg-white w-[270px] h-[500px] top-[10px] left-[10px] region-list" v-if="formData.pick_up_type.indexOf('local_delivery') != -1">
<el-scrollbar>
<div class="p-[10px] region-item pr-[50px] relative" v-for="(item, index) in formData.area" :key="index" :class="{ '!border-primary': index == currArea }" @click="selectArea(index)">
<el-form label-width="80px" :model="item" :rules="formRules" class="page-form"
ref="areaFromRef">
<div class="pb-[18px]">
<el-form-item :label="t('areaName')" prop="area_name">
<el-input v-model.trim="formData.area[index].area_name" type="text" @click.stop=""/>
</el-form-item>
</div>
<div class="pb-[18px]">
<el-form-item :label="t('startPrice')" prop="start_price">
<el-input v-model.trim="formData.area[index].start_price" maxlength="8" type="text"
@keyup="filterDigit($event)" @click.stop="" />
</el-form-item>
</div>
<div class="pb-[10px]" v-show="initData.fee_type == 'region'">
<el-form-item :label="t('deliveryPrice')" prop="delivery_price">
<el-input v-model.trim="formData.area[index].delivery_price" type="text"
@keyup="filterDigit($event)" @click.stop=""/>
</el-form-item>
</div>
<el-form-item :label="t('areaType')">
<el-radio-group v-model="formData.area[index].area_type" @change="areaTypeChange(index)">
<el-radio label="radius" size="large" class="!mr-[10px]">{{ t('radius') }}</el-radio>
<el-radio label="custom" size="large" class="!mr-[0px]">{{ t('custom') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-button type="primary" link class="absolute z-1 top-[10px] right-[10px]"
@click.stop="deleteArea(index)">{{ t('delete') }}</el-button>
</div>
<div class="p-[10px] text-center">
<el-button plain @click="addArea">{{ t('addDeliveryArea') }}</el-button>
</div>
</el-scrollbar>
</div>
</div>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)" :disabled="loading">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, onMounted, onBeforeUnmount, watch } from 'vue'
import { t } from '@/lang'
import { ArrowLeft } from '@element-plus/icons-vue'
import { useRoute, useRouter } from 'vue-router'
import { guid, filterNumber, filterDigit, deepClone, debounce } from '@/utils/common'
import { getMap, getAreaListByPid, getAreaByCode } from '@/app/api/sys'
import { createCircle, deleteGeometry, createPolygon, selectGeometry, createMarker, latLngToAddress, addressToLatLng } from '@/utils/qqmap'
import { getDeliveryStoreDetail, addDeliveryStore, editDeliveryStore, getStoreInit, getStorePickUpType } from '@/addon/shop/api/delivery'
import { FormInstance, ElMessage } from 'element-plus'
import Test from '@/utils/test'
const route = useRoute()
const router = useRouter()
const loading = ref(true)
const pageName = route.meta.title
const formRef = ref<FormInstance>()
const areaFromRef: any = ref<FormInstance[]>()
const initialFormData = {
store_id: '',
pick_up_type: [],
store_name: '',
contact_name: '',
store_mobile: '',
status: 1,
province_id: 0,
city_id: 0,
district_id: 0,
address: '',
full_address: '',
latitude: 39.908626,
longitude: 116.397190,
trade_time: '',
time_is_open: 1,
time_week: ['1', '2', '3', '4', '5', '6', '0'],
area: [
{
area_name: '',
area_type: 'radius',
start_price: 0,
delivery_price: 0,
area_json: {
key: guid()
}
}
],
trade_time_json: [
{ start_time: '', end_time: '' } //
],
time_interval: 30,
is_system: 0
}
const formData: Record<string, any> = reactive({ ...initialFormData })
formData.store_id = route.query.store_id
const pickUpType = ref([])
const initData = reactive<any>({
fee_type: 'region',
base_dist: '',
base_price: '',
grad_dist: '',
grad_price: '',
weight_start: 0.000,
weight_unit: 0,
weight_price: 0
})
const flash = ref(false)
const getStorePickUpTypeFn = () => {
getStorePickUpType().then(res => {
pickUpType.value = res.data
})
}
getStorePickUpTypeFn()
const getDeliveryStoreDetailFn = () => {
getDeliveryStoreDetail(formData.store_id).then(({ data }) => {
loading.value = false
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
if (key == 'trade_time_json' && Array.isArray(data[key])) {
formData[key] = data[key].map((item: any) => ({
start_time: timestampTransition(item.start_time),
end_time: timestampTransition(item.end_time)
}))
}
})
}).catch(() => {
loading.value = false
})
}
if (route.query.store_id) {
getDeliveryStoreDetailFn()
} else {
loading.value = false
}
const refreshAddress = (bool = false) => {
getDeliveryStoreDetail(formData.store_id).then(({ data }) => {
loading.value = false
Object.keys(formData).forEach((key: string) => {
const addressKeyArr = ['store_name', 'contact_name', 'store_mobile', 'trade_time', 'province_id', 'city_id', 'district_id', 'address', 'full_address', 'latitude', 'longitude']
if (addressKeyArr.includes(key)) {
if (data[key] != undefined) formData[key] = data[key]
}
})
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success'
})
}
}).catch(() => {
loading.value = false
})
}
const week_list = ref({})
const time_interval_list = ref({})
const getStoreInitFn = () => {
getStoreInit().then(res => {
week_list.value = res.data.week_list
time_interval_list.value = res.data.time_interval_list
Object.keys(initData).forEach((key: string) => {
if (res.data.local_delivery_config[key] != undefined) initData[key] = res.data.local_delivery_config[key]
})
})
}
interface areaType{
province: any[],
city: any[],
district: any[]
}
const areaList = reactive<areaType>({
province: [],
city: [],
district: []
})
const provinceRef = ref()
const cityRef = ref()
const districtRef = ref()
/**
* 获取省
*/
getAreaListByPid(0).then(res => {
areaList.province = res.data
})
let mapKey: string = ''
onMounted(() => {
const mapScript = document.createElement('script')
getMap().then(res => {
mapKey = res.data.key
mapScript.type = 'text/javascript'
mapScript.src = 'https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=' + res.data.key
document.body.appendChild(mapScript)
})
mapScript.onload = () => {
setTimeout(() => {
initMap()
}, 500)
}
getStoreInitFn()
})
/**
* 初始化地图
*/
let map: any
let marker: any
const mapLoading = ref(true)
const initMap = () => {
const TMap = (window as any).TMap
const LatLng = TMap.LatLng
const center = new LatLng(formData.latitude, formData.longitude)
map = new TMap.Map('container', {
center,
zoom: 14
})
map.on('tilesloaded', () => {
mapLoading.value = false
})
marker = createMarker(map)
map.on('click', (evt: any) => {
map.setCenter(evt.latLng)
marker.updateGeometries({
id: 'center',
position: evt.latLng
})
latLngChange(evt.latLng.lat, evt.latLng.lng)
})
latLngChange(center.lat, center.lng)
if (formData.pick_up_type.indexOf('local_delivery') != -1) {
formData.area.forEach(item => {
item.area_type == 'radius' ? createCircle(map, item.area_json) : createPolygon(map, item.area_json)
})
}
}
const storeArea = reactive({
province_id: 0,
city_id: 0,
district_id: 0
})
const latLngChange = (lat: number, lng: number) => {
latLngToAddress({ mapKey, lat, lng }).then(({ message, result }) => {
if (message == 'query ok' || message == 'Success') {
formData.latitude = result.location.lat
formData.longitude = result.location.lng
formData.address = result.formatted_addresses && result.formatted_addresses.recommend ? result.formatted_addresses.recommend : ''
getAreaByCode(result.ad_info.adcode).then(({ data }) => {
storeArea.province_id = data.province ? data.province.id : 0
storeArea.city_id = data.city ? data.city.id : (data.district ? data.district.id : 0)
storeArea.district_id = data.city ? (data.district ? data.district.id : 0) : 0
})
} else {
console.error(message, result)
}
}).catch(() => {
// console.log(err)
})
}
/**
* 获取市
*/
watch(() => formData.province_id, (nval) => {
if (nval) {
getAreaListByPid(formData.province_id).then(res => {
areaList.city = res.data
const cityId = formData.city_id
if (cityId) {
let isExist = false
for (let i = 0; i < res.data.length; i++) {
if (cityId == res.data[i].id) {
isExist = true
break
}
}
if (isExist) {
formData.city_id = cityId
return
}
}
formData.city_id = 0
areaChange()
})
} else {
formData.city_id = 0
}
})
/**
* 获取区
*/
watch(() => formData.city_id, (nval) => {
if (nval) {
getAreaListByPid(formData.city_id).then(res => {
areaList.district = res.data
const districtId = formData.district_id
if (districtId) {
let isExist = false
for (let i = 0; i < res.data.length; i++) {
if (districtId == res.data[i].id) {
isExist = true
break
}
}
if (isExist) {
formData.district_id = districtId
return
}
}
areaChange()
formData.district_id = 0
})
} else {
formData.district_id = 0
}
})
watch(() => formData.district_id, (nval) => {
if (nval) {
areaChange()
}
})
const handleFlash = () => {
flash.value = true
}
const areaChange = debounce(() => {
setTimeout(() => {
const province = areaList.province.map((item) => { if (item.id == formData.province_id) { return item.name } })
const city = areaList.city.map((item) => { if (item.id == formData.city_id) { return item.name } })
const district = areaList.district.map((item) => { if (item.id == formData.district_id) { return item.name } })
const address = [
formData.province_id ? (provinceRef.value.selectedLabel || province) : '',
formData.city_id ? (cityRef.value.selectedLabel || city) : '',
formData.district_id ? (districtRef.value.selectedLabel || district) : '',
formData.address
]
addressToLatLng({ mapKey, address: address.join('') }).then(({ message, result }) => {
if (message == 'Success' || message == 'query ok') {
const latLng = new (window as any).TMap.LatLng(result.location.lat, result.location.lng)
map.setCenter(latLng)
marker.updateGeometries({
id: 'center',
position: latLng
})
formData.latitude = result.location.lat
formData.longitude = result.location.lng
if (formData.pick_up_type.indexOf('local_delivery') != -1 && flash.value) {
formData.area.forEach(item => {
//
deleteGeometry(item.area_json.key)
//
item.area_json.center = {
lat: result.location.lat,
lng: result.location.lng
}
item.area_type == 'radius' ? createCircle(map, item.area_json) : createPolygon(map, item.area_json)
})
}
} else {
console.error(message, result)
}
})
}, 500)
}, 500)
/**
* 地图点选获取市
*/
watch(() => storeArea.province_id, (nval) => {
if (nval) {
getAreaListByPid(storeArea.province_id).then(res => {
areaList.city = res.data
formData.province_id = storeArea.province_id
formData.city_id = storeArea.city_id
})
}
})
/**
* 地图点选获取区
*/
watch(() => storeArea.city_id, (nval) => {
if (nval) {
getAreaListByPid(storeArea.city_id).then(res => {
areaList.district = res.data
formData.city_id = storeArea.city_id
formData.district_id = storeArea.district_id
})
}
})
/**
* 地图点选获取区
*/
watch(() => storeArea.district_id, (nval) => {
if (nval) {
formData.district_id = storeArea.district_id
}
})
//
const regExp = {
required: /[\S]+/,
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
special: /^\d{0,10}(.?\d{0,3})$/
}
//
const formRules = reactive({
store_name: [
{ required: true, message: t('storeNamePlaceholder'), trigger: 'blur' }
],
contact_name: [
{ required: true, message: t('contactNamePlaceholder'), trigger: 'blur' }
],
store_mobile: [
{ required: true, message: t('storeMobilePlaceholder'), trigger: 'blur' }
],
trade_time: [
{ required: true, message: t('tradeTimePlaceholder'), trigger: 'blur' }
],
time_week: [
{ required: true, message: t('timeWeekRequire'), trigger: 'change' }
],
distance: [
{
validator: (rule: any, value: any, callback: any) => {
if (initData.fee_type == 'distance') {
if (Test.require(formData.base_dist)) {
callback(new Error(t('baseDistRequire')))
} else if (Number(formData.base_dist) <= 0) {
callback(new Error(t('起始公里数不能小于等于0')))
}
if (Test.require(formData.base_price)) {
callback(new Error(t('basePriceRequire')))
}
if (Test.require(formData.grad_dist)) {
callback(new Error(t('gradDistRequire')))
} else if (Number(formData.grad_dist) <= 0) {
callback(new Error(t('超出公里数不能小于等于0')))
}
if (Test.require(formData.grad_price)) {
callback(new Error(t('gradPriceRequire')))
}
if (Number(formData.weight_start) <= 0) { // 0
callback(new Error(t('商品重量不能小于等于0')))
}
if (Number(formData.weight_unit) <= 0) { // 0
callback(new Error(t('商品超出重量不能小于等于0')))
}
}
callback()
},
trigger: ['blur', 'change']
}
],
weight: [
{
validator: (rule: any, value: any, callback: any) => {
if (initData.fee_type == 'distance') {
if (Number(formData.weight_start) <= 0) { // 0
callback(new Error(t('商品重量不能小于等于0')))
}
if (Number(formData.weight_unit) <= 0) { // 0
callback(new Error(t('商品超出重量不能小于等于0')))
}
}
callback()
},
trigger: ['blur', 'change']
}
],
area_name: [{ required: true, message: t('areaNameRequire'), trigger: 'blur' }],
start_price: [
{ required: true, message: t('startPriceRequire'), trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
if (parseInt(value) < 0) {
callback(new Error(t('startPriceMin')))
}
callback()
},
trigger: 'blur'
}
],
delivery_price: [
{ required: initData.fee_type == 'region', message: t('deliveryPriceRequire'), trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
if (parseInt(value) < 0) {
callback(new Error(t('deliveryPriceMin')))
}
callback()
},
trigger: 'blur'
}
],
area: [
{
validator: (rule: any, value: any, callback: any) => {
if (Test.empty(formData.area)) {
callback(new Error(t('areaPlaceholder')))
}
callback()
},
trigger: 'blur'
}
],
trade_time_json: [
{
validator: (rule: any, value: any, callback: any) => {
if (!value || value.length === 0) {
return callback(new Error(t('tradeTimePlaceholderTwo')))
}
for (let i = 0; i < value.length; i++) {
const timeRange = value[i]
if (!timeRange.start_time || !timeRange.end_time) {
return callback(new Error(t('tradeTimePlaceholderTwo')))
}
//
if (timeRange.end_time <= timeRange.start_time) {
return callback(new Error(t('tradeTimePlaceholderFour')))
}
//
if (i > 0 && value[i].start_time < value[i - 1].end_time) {
return callback(new Error(t('tradeTimePlaceholderFive')))
}
}
callback()
},
trigger: 'change',
required: true
}
],
time_interval: [
{ required: true, message: t('tradeTimePlaceholderThree'), trigger: 'change' }
],
address_area: [
{
validator: (rule: any, value: any, callback: any) => {
if (!formData.province_id) {
callback(new Error(t('provincePlaceholder')))
}
if (!formData.city_id) {
callback(new Error(t('cityPlaceholder')))
}
if (areaList.district.length && !formData.district_id) {
callback(new Error(t('districtPlaceholder')))
}
callback()
}
}
],
address: [
{ required: true, message: t('addressPlaceholder'), trigger: 'blur' }
]
})
//
const addTimeRange = () => {
formData.trade_time_json.push({ start_time: '', end_time: '' })
}
//
const removeTimeRange = (index: number) => {
formData.trade_time_json.splice(index, 1)
}
const timeTransition = (time:any) => {
const arr = time.split(':')
const num = arr[0] * 60 * 60 + arr[1] * 60
return num
}
const timestampTransition = (timeStamp:any) => {
let hour = Math.floor(timeStamp / (60 * 60))
let minute = Math.floor(timeStamp / 60) - (hour * 60)
hour = hour < 10 ? ('0' + hour) : hour
minute = minute < 10 ? ('0' + minute) : minute
return hour + ':' + minute
}
const currArea = ref<number>(0)
/**
* 添加配送区域
*/
const addArea = () => {
formData.area.push({
area_name: '',
area_type: 'radius',
start_price: 0,
delivery_price: 0,
area_json: {
key: guid()
}
})
const index = formData.area.length - 1
createCircle(map, formData.area[index].area_json)
}
/**
* 删除配送区域
*/
const deleteArea = (index: number) => {
const data = formData.area[index]
deleteGeometry(data.area_json.key)
formData.area.splice(index, 1)
}
const selectArea = (index: number) => {
currArea.value = index
const data = formData.area[index]
selectGeometry(data.area_json.key)
}
const areaTypeChange = (index: number) => {
const data = formData.area[index]
deleteGeometry(data.area_json.key)
data.area_type == 'radius' ? createCircle(map, data.area_json) : createPolygon(map, data.area_json)
}
onBeforeUnmount(() => {
map.destroy()
})
//
const changePickUpType = (val: any) => {
if (val.indexOf('local_delivery') == -1) {
formData.area.forEach((item: any) => {
deleteGeometry(item.area_json.key)
})
} else {
formData.area.forEach((item: any) => {
deleteGeometry(item.area_json.key)
item.area_type == 'radius' ? createCircle(map, item.area_json) : createPolygon(map, item.area_json)
})
}
}
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
let areaValidate = true
for (let i = 0; i < areaFromRef.value?.length; i++) {
const ref = areaFromRef.value[i]
await ref.validate(async (valid) => {
areaValidate = valid
})
if (!areaValidate) break
}
if (!areaValidate) return
if (valid) {
loading.value = true
await formEl.validate(async (valid) => {
const param: any = deepClone(formData)
formData.province_name = formData.province_id ? provinceRef.value.selectedLabel : ''
formData.city_name = formData.city_id ? cityRef.value.selectedLabel : ''
formData.district_name = formData.district_id ? districtRef.value.selectedLabel : ''
const address = [
param.province_id ? provinceRef.value.selectedLabel : '',
param.city_id ? cityRef.value.selectedLabel : '',
param.district_id ? districtRef.value.selectedLabel : '',
param.address
]
param.full_address = address.join('')
param.trade_time_json = param.trade_time_json.map((range: any) => ({
start_time: range.start_time ? timeTransition(range.start_time) : null,
end_time: range.end_time ? timeTransition(range.end_time) : null
}))
const api = formData.store_id ? editDeliveryStore : addDeliveryStore
api(param).then(() => {
loading.value = false
router.push({ path: '/shop/delivery_store' })
}).catch(() => {
loading.value = false
})
})
}
})
}
const back = () => {
router.push({ path: '/shop/delivery_store' })
}
//
const newWindow = () => {
const url = router.resolve({
path: '/shop/setting'
})
window.open(url.href)
}
</script>
<style lang="scss" scoped>
.region-list {
border: 1px solid var(--el-border-color-lighter);
z-index: 3;
.region-item {
border: 1px solid transparent;
border-bottom-color: var(--el-border-color-lighter);
}
}
#container :deep(div){
z-index: 2 !important;
}
</style>

View File

@ -0,0 +1,160 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="right">{{ pageName }}</span>
<el-button type="primary" @click="addEvent">{{ t('添加提货点') }}</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="deliveryStoreTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('提货点名称')" prop="store_name">
<el-input v-model.trim="deliveryStoreTable.searchParam.store_name" :placeholder="t('请输入提货点名称')" />
</el-form-item>
<el-form-item :label="t('提货类型')" prop="pick_up_type">
<el-select v-model="deliveryStoreTable.searchParam.pick_up_type" :placeholder="t('请选择提货类型')" clearable>
<el-option v-for="(item,key) in pickUpType" :key="key" :label="item" :value="key" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadDeliveryStoreList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table :data="deliveryStoreTable.data" size="large" v-loading="deliveryStoreTable.loading">
<template #empty>
<span>{{ !deliveryStoreTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column :label="t('提货点编号')" min-width="200" :show-overflow-tooltip="true">
<template #default="{ row }">
<el-tag size="small" v-if="row.is_system">{{ t('系统') }}</el-tag>
<span class="ml-[8px]">{{row.store_no}}</span>
</template>
</el-table-column>
<el-table-column prop="store_name" :label="t('提货点名称')" min-width="120" />
<el-table-column prop="contact_name" :label="t('联系人姓名')" min-width="80" />
<el-table-column prop="store_mobile" :label="t('联系人电话')" min-width="80" />
<el-table-column prop="full_address" :label="t('提货地址')" min-width="120" />
<el-table-column prop="trade_time" :label="t('营业时间')" min-width="120" />
<el-table-column prop="pick_up_type_name" :label="t('提货类型')" min-width="120" />
<el-table-column :label="t('开启状态')" min-width="120">
<template #default="{ row }">
<el-tag class="cursor-pointer" :type="row.status ? 'success' : 'danger'" @click="updateStatus(row)">{{ row.status ? '开启' : '关闭' }}</el-tag>
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" min-width="120" align="right">
<template #default="{ row }">
<el-button type="primary" link @click="deliverySetEvent(row)">配送服务商设置</el-button>
<el-button type="primary" link @click="editEvent(row)">编辑</el-button>
<el-button type="primary" link @click="deleteEvent(row)" v-if="row.is_system == 0">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="deliveryStoreTable.page" v-model:page-size="deliveryStoreTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="deliveryStoreTable.total"
@size-change="loadDeliveryStoreList()" @current-change="loadDeliveryStoreList" />
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { useRoute, useRouter } from 'vue-router'
import { ElMessageBox, FormInstance } from 'element-plus'
import { getDeliveryStoreList, getStorePickUpType, deleteDeliveryStore, editDeliveryStoreStatus } from '@/addon/shop/api/delivery'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const deliveryStoreTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
store_name: '',
pick_up_type: '',
create_time: ''
}
})
const searchFormRef = ref<FormInstance>()
const pickUpType = ref([])
const getStorePickUpTypeFn = () => {
getStorePickUpType().then(res => {
pickUpType.value = res.data
})
}
getStorePickUpTypeFn()
/**
* 获取门店自提列表
*/
const loadDeliveryStoreList = (page: number = 1) => {
deliveryStoreTable.loading = true
deliveryStoreTable.page = page
getDeliveryStoreList({
page: deliveryStoreTable.page,
limit: deliveryStoreTable.limit,
...deliveryStoreTable.searchParam
}).then((res: any) => {
deliveryStoreTable.loading = false
deliveryStoreTable.data = res.data.data
deliveryStoreTable.total = res.data.total
}).catch(() => {
deliveryStoreTable.loading = false
})
}
loadDeliveryStoreList()
/**
* 添加门店
*/
const addEvent = () => {
router.push('/shop/delivery_store/edit')
}
//
const deliverySetEvent = (data: any) => {
router.push('/shop/delivery_store/delivery_set?id=' + data.store_id)
}
//
const editEvent = (row: any) => {
router.push({ path: '/shop/delivery_store/edit', query: { store_id: row.store_id } })
}
//
const deleteEvent = (row: any) => {
ElMessageBox.confirm(t('确定要删除该门店吗'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
deleteDeliveryStore(row.store_id).then(() => {
loadDeliveryStoreList()
})
}).catch(() => {})
}
//
const updateStatus = (row: any) => {
row.status = row.status ? 0 : 1
editDeliveryStoreStatus({
store_id: row.store_id,
status: row.status
}).then(() => {
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadDeliveryStoreList()
}
</script>

View File

@ -124,6 +124,18 @@
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('图片展示模式') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('模式')">
<el-radio-group v-model="diyStore.editComponent.mode">
<el-radio label="aspectFill">{{ t('短边完整,长边裁剪') }}</el-radio>
<el-radio label="aspectFit">{{ t('长边适配,等比缩放') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t("goodsShowContent") }}</h3>
<el-form label-width="90px" class="px-[10px]">

View File

@ -32,6 +32,17 @@
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('图片展示模式') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('模式')">
<el-radio-group v-model="diyStore.editComponent.mode">
<el-radio label="aspectFill">{{ t('短边完整,长边裁剪') }}</el-radio>
<el-radio label="aspectFit">{{ t('长边适配,等比缩放') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t("manyGoodsListCategorySet") }}</h3>
<el-form label-width="80px" class="px-[10px]">

View File

@ -64,6 +64,17 @@
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('图片展示模式') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('模式')">
<el-radio-group v-model="diyStore.editComponent.mode">
<el-radio label="aspectFill">{{ t('短边完整,长边裁剪') }}</el-radio>
<el-radio label="aspectFit">{{ t('长边适配,等比缩放') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
</div>
<!-- 样式 -->

View File

@ -0,0 +1,197 @@
<template>
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('标题内容') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('标题风格')">
<el-radio-group v-model="diyStore.editComponent.titleStyle.way">
<el-radio :value="'text'">{{ t('文字') }}</el-radio>
<el-radio :value="'img'">{{ t('图片') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('标题')" v-show="diyStore.editComponent.titleStyle.way == 'text'">
<el-input v-model.trim="diyStore.editComponent.titleStyle.text" :placeholder="t('请输入标题')" clearable maxlength="10" show-word-limit />
</el-form-item>
<el-form-item :label="t('image')" v-show="diyStore.editComponent.titleStyle.way == 'img'">
<upload-image v-model="diyStore.editComponent.titleStyle.imgUrl" :limit="1" />
</el-form-item>
<el-form-item :label="t('link')">
<diy-link v-model="diyStore.editComponent.titleStyle.link" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('副标题内容') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('副标题')">
<el-input v-model.trim="diyStore.editComponent.subTitleStyle.text" :placeholder="t('请输入副标题')" clearable maxlength="10" show-word-limit />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('按钮内容') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('按钮名称')">
<el-input v-model.trim="diyStore.editComponent.btnStyle.text" :placeholder="t('请输入按钮名称')" clearable maxlength="4" show-word-limit />
</el-form-item>
<el-form-item :label="t('link')">
<diy-link v-model="diyStore.editComponent.btnStyle.link" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t("selectSource") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('商品标签')">
<el-input v-model.trim="diyStore.editComponent.goodsStyle.labelText" :placeholder="t('请输入名称')" clearable maxlength="4" show-word-limit />
</el-form-item>
<el-form-item :label="t('选择商品')">
<el-radio-group v-model="diyStore.editComponent.goodsStyle.source" :title="t('选择商品')">
<el-radio label="all">{{ t('全部') }}</el-radio>
<el-radio label="custom">{{ t('手动选择') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsNum')" v-if="diyStore.editComponent.goodsStyle.source == 'all'">
<el-slider class="goods-list-slider" show-input v-model="diyStore.editComponent.goodsStyle.num" :min="1" max="20" size="small" />
</el-form-item>
<el-form-item :label="t('customGoods')" v-if="diyStore.editComponent.goodsStyle.source == 'custom'">
<goods-select-popup ref="goodsSelectPopupRef" v-model="diyStore.editComponent.goodsStyle.goodsIds" :min="1" :max="99" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('图片展示模式') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('模式')">
<el-radio-group v-model="diyStore.editComponent.mode">
<el-radio label="aspectFill">{{ t('短边完整,长边裁剪') }}</el-radio>
<el-radio label="aspectFit">{{ t('长边适配,等比缩放') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
</div>
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent.titleStyle.way == 'text'">
<h3 class="mb-[10px]">{{ t('标题样式') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('textFontSize')">
<el-slider v-model="diyStore.editComponent.titleStyle.fontSize" show-input size="small" class="ml-[10px] diy-nav-slider" :min="12" :max="20" />
</el-form-item>
<el-form-item :label="t('textFontWeight')">
<el-radio-group v-model="diyStore.editComponent.titleStyle.fontWeight">
<el-radio :value="'normal'">{{ t('fontWeightNormal') }}</el-radio>
<el-radio :value="'bold'">{{ t('fontWeightBold') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('textColor')">
<el-color-picker v-model="diyStore.editComponent.titleStyle.textColor" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('副标题样式') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('textFontSize')">
<el-slider v-model="diyStore.editComponent.subTitleStyle.fontSize" show-input size="small" class="ml-[10px] diy-nav-slider" :min="12" :max="30" />
</el-form-item>
<el-form-item :label="t('textColor')">
<el-color-picker v-model="diyStore.editComponent.subTitleStyle.textColor" />
</el-form-item>
<el-form-item :label="t('背景色')">
<el-color-picker v-model="diyStore.editComponent.subTitleStyle.bgColor" />
</el-form-item>
<el-form-item :label="t('标题圆角')">
<el-slider v-model="diyStore.editComponent.subTitleStyle.rounded" show-input size="small" class="ml-[10px] diy-nav-slider" :min="0" :max="30" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('按钮样式') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('文字大小')">
<el-slider v-model="diyStore.editComponent.btnStyle.fontSize" show-input size="small" class="ml-[10px] diy-nav-slider" :min="10" :max="15" />
</el-form-item>
<el-form-item :label="t('文字颜色')">
<el-color-picker v-model="diyStore.editComponent.btnStyle.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('背景色')">
<el-color-picker v-model="diyStore.editComponent.btnStyle.startBgColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
<el-color-picker v-model="diyStore.editComponent.btnStyle.endBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('渐变方式')">
<el-radio-group v-model="diyStore.editComponent.btnStyle.gradientType">
<el-radio :value="'linear'">{{ t('线性渐变') }}</el-radio>
<el-radio :value="'radial'">{{ t('径向渐变') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('商品样式') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('标签大小')">
<el-slider v-model="diyStore.editComponent.goodsStyle.labelSize" show-input size="small" class="ml-[10px] diy-nav-slider" :min="10" :max="15" />
</el-form-item>
<el-form-item :label="t('标签文字色')">
<el-color-picker v-model="diyStore.editComponent.goodsStyle.labelColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('标签背景色')">
<el-color-picker v-model="diyStore.editComponent.goodsStyle.labelBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('背景色')">
<el-color-picker v-model="diyStore.editComponent.goodsStyle.bgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('商品圆角')">
<el-slider v-model="diyStore.editComponent.goodsStyle.rounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('图片圆角')">
<el-slider v-model="diyStore.editComponent.goodsStyle.imgRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
<script lang="ts" setup>
import { t } from '@/lang'
import useDiyStore from '@/stores/modules/diy'
import { ref, reactive, onMounted } from 'vue'
import goodsSelectPopup from '@/addon/shop/views/goods/components/goods-select-popup.vue'
const diyStore: any = useDiyStore()
diyStore.editComponent.ignore = [] //
//
diyStore.editComponent.verify = (index: number) => {
const res = { code: true, message: '' }
if (diyStore.value[index].goodsStyle.source == 'custom' && !diyStore.value[index].goodsStyle.goodsIds.length) {
res.code = false
res.message = t('请选择商品')
}
return res
}
defineExpose({})
</script>
<style lang="scss">
.goods-list-slider {
.el-slider__input {
width: 100px;
}
}
</style>

View File

@ -59,6 +59,17 @@
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('图片展示模式') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('模式')">
<el-radio-group v-model="diyStore.editComponent.mode">
<el-radio label="aspectFill">{{ t('短边完整,长边裁剪') }}</el-radio>
<el-radio label="aspectFit">{{ t('长边适配,等比缩放') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
</div>
<!-- 样式 -->

View File

@ -15,6 +15,17 @@
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('图片展示模式') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('模式')">
<el-radio-group v-model="diyStore.editComponent.mode">
<el-radio label="aspectFill">{{ t('短边完整,长边裁剪') }}</el-radio>
<el-radio label="aspectFit">{{ t('长边适配,等比缩放') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<el-form label-width="120px" class="px-[10px]">

View File

@ -32,6 +32,17 @@
</el-dialog>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('图片展示模式') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('模式')">
<el-radio-group v-model="diyStore.editComponent.mode">
<el-radio label="aspectFill">{{ t('短边完整,长边裁剪') }}</el-radio>
<el-radio label="aspectFit">{{ t('长边适配,等比缩放') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('titleContent') }}</h3>

View File

@ -45,6 +45,17 @@
</el-dialog>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('图片展示模式') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('模式')">
<el-radio-group v-model="diyStore.editComponent.mode">
<el-radio label="aspectFill">{{ t('短边完整,长边裁剪') }}</el-radio>
<el-radio label="aspectFit">{{ t('长边适配,等比缩放') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t("selectSource") }}</h3>

View File

@ -479,71 +479,7 @@ const previewEvent = (data: any) => {
}
const activeclick = (data: any) => {
if (data.list[0].join_type == 'impulse_buy') {
const routeUrl = router.resolve({
path: '/shop_impulse_buy/list',
query: {
active_id: data.list[0].join_id
}
})
window.open(routeUrl.href, '_blank')
} else if (data.list[0].join_type == 'discount') {
const routeUrl = router.resolve({
path: '/shop/marketing/discount/list',
query: {
active_id: data.list[0].join_id
}
})
window.open(routeUrl.href, '_blank')
} else if (data.list[0].join_type == 'gift_card') {
const routeUrl = router.resolve({
path: '/shop_giftcard/giftcard/list',
query: {
active_id: data.list[0].join_id
}
})
window.open(routeUrl.href, '_blank')
} else if (data.list[0].join_type == 'active_exchange') {
const routeUrl = router.resolve({
path: '/shop/marketing/exchange/goods_list',
query: {
active_id: data.list[0].join_id
}
})
window.open(routeUrl.href, '_blank')
} else if (data.list[0].join_type == 'active_newcomer_discount') {
const routeUrl = router.resolve({
path: '/shop/marketing/newcomer/config'
// query: {
// active_id: data.list[0].join_id
// }
})
window.open(routeUrl.href, '_blank')
} else if (data.list[0].join_type == 'manjian') {
const routeUrl = router.resolve({
path: '/shop/marketing/manjian/list',
query: {
active_id: data.list[0].join_id
}
})
window.open(routeUrl.href, '_blank')
} else if (data.list[0].join_type == 'seckill') {
const routeUrl = router.resolve({
path: '/seckill/active/list',
// query: {
// active_id: data.list[0].join_id
// }
})
window.open(routeUrl.href, '_blank')
}else if (data.list[0].join_type == 'pintuan') {
const routeUrl = router.resolve({
path: '/pintuan/active/list',
// query: {
// active_id: data.list[0].join_id
// }
})
window.open(routeUrl.href, '_blank')
}
window.open(data.jump_url, '_blank')
}
//
@ -783,7 +719,7 @@ loadGoodsList(getTablePageStorage(goodsTable.searchParam).page)
* 添加商品
*/
const addEvent = () => {
router.push('/shop/goods/real_edit')
router.push('/shop/goods/real_add')
// let url = router.resolve({
// path: '/shop/goods/real_edit',
// });
@ -865,6 +801,10 @@ const copyEvent = (data: any) => {
loadGoodsList(getTablePageStorage(goodsTable.searchParam).page)
}
repeat.value = false
ElMessage({
type: 'success',
message: '商品复制成功,可以在仓库中商品查询'
})
}).catch(() => {
repeat.value = false
})

View File

@ -3,7 +3,7 @@ import { t } from '@/lang'
import { TabsPaneContext, ElMessage } from 'element-plus'
import Sortable from 'sortablejs'
import { range, cloneDeep } from 'lodash-es'
import { debounce, deepClone } from '@/utils/common'
import { debounce, deepClone, getImageDimensions } from '@/utils/common'
import { useRoute, useRouter } from 'vue-router'
import Test from '@/utils/test'
import {
@ -125,7 +125,9 @@ export function useGoodsEdit(params: any = {}) {
// 切换商品类型
const changeGoodsType = (data: any) => {
router.push(data.path)
// 添加商品是切换类型不应该是edit
let url = '/shop/goods/'+data.type + '_add'
router.push(url)
}
// 商品类型
@ -1358,13 +1360,18 @@ export function useGoodsEdit(params: any = {}) {
// 保存数据
const save = (callback: any = null) => {
verify(() => {
verify(async() => {
if (repeat.value) return
repeat.value = true
const api = formData.goods_id ? editApi : addApi
const data = cloneDeep(formData)
const coverImage = data.goods_image.split(',')[0]
const info = await getImageDimensions(coverImage)
data.goods_image_width = info ? info.width : 800
data.goods_image_height = info ? info.height : 800
if (data.spec_type == 'multi') {
data.stock = 0

View File

@ -199,6 +199,7 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { t } from '@/lang'
import { ElMessage, ElNotification } from 'element-plus'
import {
getShopCountList,
getShopTodayCountList,
@ -207,6 +208,7 @@ import {
getShopOrderStat,
getShopGoodsStat
} from '@/addon/shop/api/shop'
import { checkJobStatus } from '@/app/api/sys'
import * as echarts from 'echarts'
import { useRouter } from 'vue-router'
@ -370,6 +372,27 @@ const nowTime = () => {
time.value = year + '-' + month + '-' + day + ' ' + hh + ':' + mm + ':' + ss
}
nowTime()
/**
* 校验消息队列
*/
let notification = null
const checkJob = () => {
checkJobStatus().then(res => {
if (!res.data) {
notification = ElNotification({
title: '温馨提示',
dangerouslyUseHTMLString: true,
duration: 0,
position: 'bottom-right',
message: '您的【队列】运行异常,会导致商城订单相关任务无法正常使用,请尽快启动队列<br><a class="text-[var(--el-color-primary)]" href="https://doc.press.niucloud.com/php/saas-framework/use/other/scheduled-task-config.html" target="_blank">>>> 点击查看处理方法 <<<</a>',
type: 'warning'
})
}
}).catch(() => {
})
}
checkJob()
</script>
<style lang="scss" scoped></style>

View File

@ -30,16 +30,13 @@
</el-table-column>
<el-table-column label="有效期" min-width="210">
<template #default="{ row }">
<template v-if="row.receive_type == 1">
<span v-if="row.valid_type == 1"> 领取之日起{{ row.length || '' }} 天内有效</span>
<span v-else> 使用截止时间至{{ row.valid_end_time || ''}} </span>
</template>
<span v-else>--</span>
<span v-if="row.valid_type == 1"> 领取之日起{{ row.length || '' }} 天内有效</span>
<span v-else> 使用截止时间至{{ row.valid_end_time || ''}} </span>
</template>
</el-table-column>
<el-table-column label="赠券数" min-width="130">
<template #default="{ row }">
<el-input-number
<el-input-number v-if="row.limit_count"
v-model="formData.coupon_list['id_' + row.id]"
:min="1"
:precision="0"
@ -47,6 +44,14 @@
controls-position="right"
class="!w-[100px]"
/>
<el-input-number v-else
v-model="formData.coupon_list['id_' + row.id]"
:min="1"
:max="999999"
:precision="0"
controls-position="right"
class="!w-[100px]"
/>
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" fixed="right">

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="showDialog" :title="showType == 'add' ? t('delivery') : t('修改物流信息')" :width="showType =='add' ?'700px' :'1000px'" class="diy-dialog-wrap" :destroy-on-close="true" :close-on-click-modal="false">
<el-dialog v-model="showDialog" :title="showType == 'add' ? t('delivery') : t('修改物流信息')" :width="showType =='add' ? '700px' :'1000px'" class="diy-dialog-wrap" :destroy-on-close="true" :close-on-click-modal="false" @close="handleClose">
<div v-loading="loading">
<el-alert type="warning" :closable="false" class="!mb-[10px]" v-if="isTradeManaged">
@ -35,15 +35,67 @@
</el-select>
</el-form-item>
</template>
<template v-if="formData.delivery_type == 'local_delivery'">
<el-form-item :label="t('deliveryWay')">
<el-radio-group v-model="formData.delivery_way" @change="deliveryWayChange">
<el-radio label="store_delivery">{{ t('storeDeliveryWay') }}</el-radio>
<el-radio label="third_party_delivery">{{ t('thirdPartyDeliveryWay') }}</el-radio>
</el-radio-group>
</el-form-item>
<div v-if="formData.delivery_way == 'store_delivery'">
<el-form-item :label="t('deliveryStore')" prop="store_id">
<el-select v-model="formData.store_id" :placeholder="t('deliveryStorePlaceholder')" clearable class="input-width">
<el-option v-for="(item) in deliveryStoreData" :key="item.store_id" :label="item.store_name" :value="item.store_id" />
</el-select>
</el-form-item>
<el-form-item :label="t('deliverName')" prop="local_deliver_id">
<el-select v-model="formData.local_deliver_id" :placeholder="t('deliverNamePlaceholder')" clearable class="input-width" @change="deliverNameChange">
<el-option v-for="(item) in deliverData" :key="item.deliver_id" :label="item.deliver_name" :value="item.deliver_id" />
</el-select>
</el-form-item>
<el-form-item :label="t('deliverMobile')" prop="deliver_mobile">
<el-input v-model.trim="formData.deliver_mobile" clearable :disabled="true" :placeholder="t('deliverMobilePlaceholder')" class="input-width" maxlength="30" />
</el-form-item>
</div>
<div v-if="formData.delivery_way == 'third_party_delivery'">
<el-form-item :label="t('deliveryStore')" prop="store_id">
<div>
<el-select v-model="formData.store_id" :placeholder="t('deliveryStorePlaceholder')" clearable class="input-width" @change="deliveryStoreChange">
<el-option v-for="(item) in deliveryStoreData" :key="item.store_id" :label="item.store_name" :value="item.store_id" />
</el-select>
<span class="ml-[10px] cursor-pointer text-primary" @click="toAddress" v-if="curThirdDelivery?.open_status == 2">去开通</span>
</div>
</el-form-item>
<el-form-item :label="t('thirdDeliveryName')" prop="local_delivery_type">
<div class="flex items-center">
<el-select v-model="formData.local_delivery_type" :placeholder="t('thirdDeliveryNamePlaceholder')" clearable class="input-width" @change="thirdDeliveryChange">
<el-option v-for="(item,index) in thirdDeliveryData" :key="index" :label="item.name" :value="item.key" />
</el-select>
<div class="ml-[10px]">
<span class="cursor-pointer text-primary mr-[10px]" @click="getInUseLocalDeliveryListFn(formData.store_id)">{{ t('refresh') }}</span>
<span class="cursor-pointer text-primary" @click="toThirdDelivery">{{ t('去配置') }}</span>
</div>
</div>
</el-form-item>
<el-form-item :label="t('goodsWeight')" prop="goods_weight">
<el-input v-model.trim="formData.goods_weight" clearable placeholder="0.000" class="input-width" maxlength="6" @blur="thirdDeliveryChange">
<template #append>kg</template>
</el-input>
</el-form-item>
<el-form-item :label="t('配送费用')" >
<div class="input-width">{{ deliveryFee }}</div>
</el-form-item>
</div>
</template>
</el-form>
<el-table :data="goodsDataArr" size="large" @selection-change="handleSelectionChange" max-height="400px">
<el-table :data="goodsDataArr" size="large" @selection-change="handleSelectionChange" ref="tableRef" max-height="400px">
<el-table-column type="selection" width="55" :selectable="selectable" />
<el-table-column prop="goods_name" :label="t('goodsName')" min-width="200" >
<template #default="{ row }">
<div class="flex cursor-pointer">
<div class="flex items-center min-w-[50px] mr-[10px]">
<img class="w-[50px] h-[50px]" v-if="row.goods_image" :src="img(row.goods_image)" alt="">
<img class="w-[50px] h-[50px]" v-else src="" alt="">
<img class="w-[50px] h-[50px]" v-else src="" alt="">
</div>
<div class="flex flex-col items-start">
<span class="multi-hidden text-[14px]">{{row.goods_name}}</span>
@ -56,12 +108,22 @@
<el-table-column prop="num" :label="t('num')" min-width="80" />
<el-table-column prop="status_name" :label="t('refundStatusName')" min-width="80" />
<el-table-column prop="delivery_status_name" :label="t('deliveryStatusName')" min-width="80" />
<el-table-column prop="express_company_name" :label="t('物流公司')" min-width="80" v-if="showType == 'edit'">
<el-table-column :label="t('deliveryPlatform')" min-width="80" v-if="showType == 'edit' && formData.delivery_type == 'local_delivery'">
<template #default="{ row }">
<span>{{row.delivery_info?.delivery_service_name }}</span>
</template>
</el-table-column>
<el-table-column prop="local_delivery_status_name" :label="t('deliveryStatus')" min-width="80" v-if="showType == 'edit' && formData.delivery_type == 'local_delivery'">
<template #default="{ row }">
<span>{{ row.delivery_info?.local_delivery_status_name }}</span>
</template>
</el-table-column>
<el-table-column prop="express_company_name" :label="t('物流公司')" min-width="80" v-if="showType == 'edit' && formData.delivery_type == 'express'">
<template #default="{ row }">
<span class="text-[#999]">{{ row.delivery_info?.express_company_name }}</span>
</template>
</el-table-column>
<el-table-column prop="express_number" :label="t('物流单号')" min-width="80" v-if="showType == 'edit'">
<el-table-column prop="express_number" :label="t('物流单号')" min-width="80" v-if="showType == 'edit' && formData.delivery_type == 'express'">
<template #default="{ row }">
<span class="text-[#999]">{{ row.delivery_info?.express_number }}</span>
</template>
@ -70,7 +132,7 @@
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button @click="handleClose">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
@ -78,21 +140,19 @@
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue'
import { ref, reactive, computed, nextTick } from 'vue'
import { t } from '@/lang'
import { img } from '@/utils/common'
import { FormInstance, ElMessage } from 'element-plus'
import { getCompanyList } from '@/addon/shop/api/delivery'
import { getOrderDeliveryType, orderDelivery } from '@/addon/shop/api/order'
import { getCompanyList, getShopDeliverList, getInUseLocalDeliveryList, getDeliveryStoreListAll } from '@/addon/shop/api/delivery'
import { getOrderDeliveryType, orderDelivery, getSelectOrderGoodsWeight, getDeliveryFee } from '@/addon/shop/api/order'
import { getIsTradeManaged } from '@/app/api/weapp'
import { cloneDeep } from 'lodash-es'
import {
getElectronicSheetConfig,
getElectronicSheetList,
printElectronicSheet
} from '@/addon/shop/api/electronic_sheet'
import { loadCLodop,getLodop } from '@/utils/lodop'
import { getElectronicSheetConfig, getElectronicSheetList, printElectronicSheet } from '@/addon/shop/api/electronic_sheet'
import { loadCLodop, getLodop } from '@/utils/lodop'
import { useRouter } from 'vue-router'
const router = useRouter()
const showDialog = ref(false)
const loading = ref(false)
interface companyDataType{
@ -139,6 +199,57 @@ const companyChange = (value: any) => {
})
}
/**
* 配送员
*/
const deliverNameChange = (value: any) => {
if (value) {
deliverData.value.forEach((item: any) => {
if (item.deliver_id == value) {
formData.deliver_mobile = item.deliver_mobile
}
})
} else {
formData.deliver_mobile = ''
formData.local_deliver_id = ''
}
}
const deliverData = ref([])
getShopDeliverList({}).then((res: any) => {
deliverData.value = res.data
})
/**
* 三方配送列表
*/
const thirdDeliveryData = ref([])
const getInUseLocalDeliveryListFn = (value: any) => {
getInUseLocalDeliveryList({store_id: value}).then((res: any) => {
thirdDeliveryData.value = res.data
})
}
/**
* 获取提货点列表
*/
const deliveryStoreData = ref([])
getDeliveryStoreListAll({
pick_up_type: 'local_delivery',
}).then((res: any) => {
deliveryStoreData.value = res.data
})
const deliveryStoreChange = (value: any) => {
if (formData.delivery_way == 'third_party_delivery' && value) {
formData.local_delivery_type = ''
deliveryFee.value = 0
getInUseLocalDeliveryListFn(value)
} else {
thirdDeliveryData.value = []
}
}
/**
* 表单数据
*/
@ -147,17 +258,30 @@ const initialFormData = {
order_id: 0,
delivery_type: '',
delivery_way: 'manual_write',
local_delivery_type: 'merchant',
goods_weight: 1,
local_deliver_id: '',
deliver_mobile: '',
express_company_id: '',
express_number: '',
electronic_sheet_id: '',
order_goods_ids: [],
delivery_ids: []
delivery_ids: [],
store_id: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const regExp: any = {
required: /[\S]+/,
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
special: /^\d{0,10}(.?\d{0,3})$/
}
//
const formRules = computed(() => {
return {
@ -172,6 +296,18 @@ const formRules = computed(() => {
],
electronic_sheet_id: [
{ required: true, validator: electronicSheetIdPass, trigger: 'blur' }
],
local_deliver_id: [
{ required: true, validator: localDeliverIdPass, trigger: 'blur' }
],
store_id: [
{ required: true, validator: storeIdPass, trigger: 'blur' }
],
local_delivery_type: [
{ required: true, validator: thirdDeliveryTypePass, trigger: ['blur', 'change'] }
],
goods_weight: [
{ required: true, validator: goodsWeightPass, trigger: 'blur' }
]
}
})
@ -200,8 +336,50 @@ const electronicSheetIdPass = (rule: any, value: any, callback: any) => {
}
}
const localDeliverIdPass = (rule: any, value: any, callback: any) => {
if (formData.delivery_type == 'local_delivery' && formData.delivery_way == 'store_delivery' && value == '') {
callback(new Error(t('deliverNamePlaceholder')))
} else {
callback()
}
}
const storeIdPass = (rule: any, value: any, callback: any) => {
if (formData.delivery_type == 'local_delivery' && value == '') {
callback(new Error(t('deliveryStorePlaceholder')))
} else {
callback()
}
}
const thirdDeliveryTypePass = (rule: any, value: any, callback: any) => {
if (formData.delivery_type == 'local_delivery' && formData.delivery_way == 'third_party_delivery' && (value == '' || value == undefined)) {
callback(new Error(t('thirdDeliveryNamePlaceholder')))
} else {
callback()
}
}
const goodsWeightPass = (rule: any, value: any, callback: any) => {
if (formData.delivery_type == 'local_delivery' && formData.delivery_way == 'third_party_delivery' && value == '') {
callback(new Error(t('goodsWeightPlaceholder')))
} else if (isNaN(value) || !regExp.special.test(value)) {
callback(new Error(t('weightTips')))
} else if (value <= 0) {
callback(new Error(t('weightNotZeroTips')))
} else {
callback()
}
}
const selectable = (row:any, index:number) => {
if ((row.status == 2 || row.delivery_status != 'wait_delivery' || row.status == 3 || row.is_gift == 1 )&& (showType.value =='add' || !showType.value)) {
if ((row.status == 2 || row.delivery_status != 'wait_delivery' || row.status == 3 || row.is_gift == 1) && (showType.value == 'add' || !showType.value)) {
return false
}
if (row.delivery_info && row.delivery_info.delivery_service == 'dada' && (row.delivery_info.local_delivery_status == '-2' || row.delivery_info.local_delivery_status == '-1' || row.delivery_info.local_delivery_status == '8')) {
return false
}
if (formData.delivery_type == 'local_delivery') {
return false
}
return true
@ -226,6 +404,20 @@ const deliveryChange = () => {
})
}
goodsDataArr.value = cloneDeep(arr)
if (formData.delivery_type && formData.delivery_type == 'express') {
formData.delivery_way = 'manual_write'
} else if (formData.delivery_type && formData.delivery_type == 'local_delivery') {
formData.delivery_way = 'store_delivery'
}
}
const deliveryWayChange = () => {
curThirdDelivery.value = {}
if (formData.delivery_way && formData.delivery_way == 'store_delivery') {
formData.local_delivery_type = 'merchant'
} else {
formData.local_delivery_type = ''
}
}
const handleSelectionChange = (val:any) => {
@ -233,11 +425,35 @@ const handleSelectionChange = (val:any) => {
formData.delivery_ids = cloneDeep([])
for (const v in val) {
formData.order_goods_ids.push(val[v].order_goods_id)
if(showType.value == 'edit'){
if (showType.value == 'edit') {
formData.delivery_ids.push(val[v].delivery_id)
}
}
}
const getSelectOrderGoodsWeightFn = () => {
getSelectOrderGoodsWeight({
order_goods_ids: formData.order_goods_ids
}).then((res: any) => {
formData.goods_weight = res.data
})
}
//
const deliveryFee = ref<any>(0)
const getDeliveryFeeFn = () => {
getDeliveryFee(formData).then((res: any) => {
deliveryFee.value = res.data
})
}
const curThirdDelivery = ref<any>(null)
const thirdDeliveryChange = (value: any) => {
if (formData.delivery_way == 'third_party_delivery' && value) {
curThirdDelivery.value = thirdDeliveryData.value?.find((item: any) => item.key == value)
getDeliveryFeeFn()
} else {
deliveryFee.value = 0
}
}
const emit = defineEmits(['complete'])
@ -276,6 +492,7 @@ const confirm = async (formEl: FormInstance | undefined) => {
})
}
const showType = ref('')
const tableRef = ref()
const setFormData = async (row: any = null,type:any) => {
loading.value = true
showType.value = type
@ -283,6 +500,9 @@ const setFormData = async (row: any = null,type:any) => {
formData.order_id = row.order_id
formData.delivery_type = ''
formData.delivery_way = 'manual_write'
formData.local_delivery_type = 'merchant'
formData.store_id = row.take_store_id
formData.local_deliver_id = row.order_goods[0]?.delivery_info?.local_deliver_id
goodsData.value = row.order_goods
goodsDataArr.value = row.order_goods
isHasVirtual.value = false
@ -306,6 +526,25 @@ const setFormData = async (row: any = null,type:any) => {
if (isHasVirtual.value) {
Object.assign(deliveryType.value, { virtual: t('virtualDelivery') })
}
if (formData.delivery_type == 'local_delivery') {
nextTick(() => {
if (tableRef.value) {
if (row.status == 2) {
goodsDataArr.value.forEach((item: any, index: any) => {
tableRef.value.toggleRowSelection(item, true)
})
} else if (row.status == 3) {
goodsDataArr.value.forEach((item: any, index: any) => {
if (item.delivery_info.local_delivery_status == '-2' || item.delivery_info.local_delivery_status == '-1' || item.delivery_info.local_delivery_status == '8') {
tableRef.value.toggleRowSelection(item, true)
}
})
}
}
getSelectOrderGoodsWeightFn()
getInUseLocalDeliveryListFn(formData.store_id)
})
}
}
loading.value = false
}
@ -313,6 +552,10 @@ const setFormData = async (row: any = null,type:any) => {
const initFormData = () => {
formData.order_id = 0
formData.delivery_type = ''
formData.delivery_way = 'manual_write'
formData.local_delivery_type = 'merchant'
formData.store_id = ''
formData.local_deliver_id = ''
formData.express_company_id = ''
formData.electronic_sheet_id = ''
formData.express_number = ''
@ -352,6 +595,28 @@ const printElectronicSheetFn = () => {
})
}
const handleClose = () => {
showDialog.value = false
initFormData()
}
//
const toThirdDelivery = () => {
const url = router.resolve({
path: '/shop/delivery/local_delivery_service'
})
window.open(url.href)
}
const toAddress = () => {
const url = router.resolve({
path: '/shop/delivery_store/delivery_set',
query: {
id: formData.store_id
}
})
window.open(url.href)
}
defineExpose({
showDialog,
setFormData

View File

@ -484,7 +484,7 @@ const deliveryRefuseEvent = () => {
const transferEvent = () => {
const routeUrl = router.resolve({
path: '/member/refund/detail',
path: '/finance/refund/detail',
query: { refund_no: formData.value.refund_no }
})
window.open(routeUrl.href, '_blank')

View File

@ -153,8 +153,11 @@
</template>
</el-table-column>
<el-table-column min-width="100">
<template #default>
<span class="text-[14px]">{{ item.delivery_type_name }}</span>
<template #default="{ row }">
<div>
<div class="text-[14px]">{{ item.delivery_type_name }}</div>
<div class="text-[14px] cursor-pointer text-primary" @click="toDelivery(item)">{{ row.delivery_info?.local_delivery_status_name }}</div>
</div>
</template>
</el-table-column>
<el-table-column min-width="100">
@ -677,6 +680,16 @@ const batchDeleteFn = () => {
})
})
}
const toDelivery = (data: any) => {
const url = router.resolve({
path: '/shop/delivery/local/record',
query: {
trade_no: data.order_no
}
})
window.open(url.href)
}
</script>
<style lang="scss" scoped>

View File

@ -11,7 +11,7 @@
namespace addon\shop\app\adminapi\controller\delivery;
use addon\shop\app\service\admin\delivery\DeliverService;
use addon\shop\app\dict\order\OrderDeliveryDict;
use addon\shop\app\service\admin\delivery\DeliveryService;
use core\base\BaseAdminController;
@ -42,7 +42,7 @@ class Delivery extends BaseAdminController
/**
* 配送页信息
* @description 获取配送页信息
* @return void
* @return \think\Response
*/
public function getDelivery()
{
@ -52,7 +52,7 @@ class Delivery extends BaseAdminController
/**
* 配送页信息
* @description 获取配送页信息配置
* @return void
* @return \think\Response
*/
public function getDeliveryList()
{
@ -60,70 +60,11 @@ class Delivery extends BaseAdminController
}
/**
* 获取配送员列表
* @description 查看配送员列表
* 获取配送方式
* @return \think\Response
*/
public function lists()
public function getDeliveryType()
{
$data = $this->request->params([
[ "deliver_name", "" ],
[ "deliver_mobile", "" ]
]);
return success(( new DeliverService() )->getPage($data));
}
/**
* 配送员详情
* @description 查看配送员详情
* @param int $id
* @return \think\Response
*/
public function info(int $id)
{
return success(( new DeliverService() )->getInfo($id));
}
/**
* 添加配送员
* @description 添加配送员
* @return \think\Response
*/
public function add()
{
$data = $this->request->params([
[ "deliver_name", "" ],
[ "deliver_mobile", "" ],
]);
$id = ( new DeliverService() )->add($data);
return success('ADD_SUCCESS', [ 'id' => $id ]);
}
/**
* 配送员编辑
* @description 编辑配送员
* @param $id 自提门店id
* @return \think\Response
*/
public function edit($id)
{
$data = $this->request->params([
[ "deliver_name", "" ],
[ "deliver_mobile", "" ],
]);
( new DeliverService() )->edit($id, $data);
return success('EDIT_SUCCESS');
}
/**
* 配送员删除
* @description 删除配送员
* @param $id
* @return \think\Response
*/
public function del(int $id)
{
( new DeliverService() )->del($id);
return success('DELETE_SUCCESS');
return success(OrderDeliveryDict::getType());
}
}

View File

@ -85,6 +85,12 @@ class ElectronicSheet extends BaseAdminController
[ 'exp_type', 0 ],
[ "print_style", "" ],
[ "is_default", 0 ],
// [ "kd100_customer_name", "" ],
// [ "kd100_customer_pwd", "" ],
// [ "kd100_send_site", "" ],
// [ "kd100_send_staff", "" ],
// [ "kd100_month_code", "" ],
// [ "kd100_pay_type", 0 ],
]);
$this->validate($data, 'addon\shop\app\validate\delivery\ElectronicSheet.add');
$id = ( new ElectronicSheetService() )->add($data);
@ -113,6 +119,12 @@ class ElectronicSheet extends BaseAdminController
[ 'exp_type', 0 ],
[ "print_style", "" ],
[ "is_default", 0 ],
// [ "kd100_customer_name", "" ],
// [ "kd100_customer_pwd", "" ],
// [ "kd100_send_site", "" ],
// [ "kd100_send_staff", "" ],
// [ "kd100_month_code", "" ],
// [ "kd100_pay_type", 0 ],
]);
$this->validate($data, 'addon\shop\app\validate\delivery\ElectronicSheet.edit');
( new ElectronicSheetService() )->edit($id, $data);
@ -156,7 +168,9 @@ class ElectronicSheet extends BaseAdminController
[ "kdniao_api_key", "" ],
[ 'server_port1', '8000' ],
[ 'server_port2', '18000' ],
[ 'https_port', '8443' ]
[ 'https_port', '8443' ],
[ "kd100_key", "" ],
[ "kd100_secret", "" ],
]);
( new ElectronicSheetService() )->setConfig($data);
return success();

View File

@ -1,148 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\adminapi\controller\delivery;
use core\base\BaseAdminController;
use addon\shop\app\service\admin\delivery\StoreService;
/**
* 自提门店控制器
* Class Store
* @package addon\shop\app\adminapi\controller\delivery
*/
class Store extends BaseAdminController
{
/**
* 获取展示用信息
* @description 获取添加/编辑展示用信息
* @param int $id
* @return \think\Response
*/
public function init()
{
return success(( new StoreService() )->getInitInfo());
}
/**
* 获取自提门店列表
* @description 查看自提门店列表详情
* @return \think\Response
*/
public function lists()
{
$data = $this->request->params([
[ "store_name", "" ],
[ "create_time", [ "", "" ] ]
]);
return success(( new StoreService() )->getPage($data));
}
/**
* 自提门店详情
* @param int $id
* @return \think\Response
*/
public function info(int $id)
{
return success(( new StoreService() )->getInfo($id));
}
/**
* 添加自提门店
* @description 添加自提门店
* @return \think\Response
*/
public function add()
{
$data = $this->request->params([
[ "store_name", "" ],
[ "store_desc", "" ],
[ "store_logo", "" ],
[ "store_mobile", 0 ],
[ "province_name", "" ],
[ "city_name", "" ],
[ "district_name", "" ],
[ "address", "" ],
[ "full_address", "" ],
[ "longitude", "" ],
[ "latitude", "" ],
[ "trade_time", "" ],
[ "time_type", "" ],//类型
[ "time_week", [] ],//周数组
[ "time_interval", "" ],//间隔时间段 单位分钟
[ "trade_time_json", [] ],//营业时间
]);
$this->validate($data, 'addon\shop\app\validate\delivery\Store.add');
$id = ( new StoreService() )->add($data);
return success('ADD_SUCCESS', [ 'id' => $id ]);
}
/**
* 自提门店编辑
* @description 编辑自提门店
* @param $id 自提门店id
* @return \think\Response
*/
public function edit($id)
{
$data = $this->request->params([
[ "store_name", "" ],
[ "store_desc", "" ],
[ "store_logo", "" ],
[ "store_mobile", 0 ],
[ "address", "" ],
[ "full_address", "" ],
[ "longitude", "" ],
[ "latitude", "" ],
[ "trade_time", "" ],
[ "time_week", [] ],//周数组
[ "time_interval", "" ],//间隔时间段 单位分钟
[ "trade_time_json", "" ],//营业时间
]);
$address = $this->request->params([
[ "province_name", "" ],
[ "city_name", "" ],
[ "district_name", "" ],
]);
$this->validate($data, 'addon\shop\app\validate\delivery\Store.edit');
( new StoreService() )->edit($id, $data, $address);
return success('EDIT_SUCCESS');
}
/**
* 自提门店删除
* @description 删除自提门店
* @param $id 自提门店id
* @return \think\Response
*/
public function del(int $id)
{
( new StoreService() )->del($id);
return success('DELETE_SUCCESS');
}
/**
* @description 查看自提门店列表-全部
* @return \think\Response
*/
public function getList()
{
$data = $this->request->params([
[ "store_name", "" ],
[ "create_time", [ "", "" ] ]
]);
return success(( new StoreService() )->getList($data));
}
}

View File

@ -0,0 +1,63 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\adminapi\controller\delivery_store;
use addon\shop\app\service\admin\delivery_store\DeliveryServiceService;
use core\base\BaseAdminController;
use think\Response;
/**
* 配送服务商控制器
* Class DeliveryService
* @package addon\shop\app\adminapi\controller\delivery_store
*/
class DeliveryService extends BaseAdminController
{
/**
* 获取配送服务商列表
*/
public function getDeliveryServiceList($id)
{
return success((new DeliveryServiceService())->deliveryServiceList($id));
}
/**
* 配送门店开通配送服务
* @return Response
*/
public function deliveryServiceOpen($id)
{
$data = $this->request->params([
['delivery_type', ''],
['business', ''],
['extend_data', []]
]);
(new DeliveryServiceService())->deliveryServiceOpen($id, $data);
return success('SUCCESS');
}
/**
* 配送门店品类修改
* @return Response
*/
public function deliveryServiceEdit($id)
{
$data = $this->request->params([
['delivery_type', ''],
['business', '']
]);
(new DeliveryServiceService())->deliveryServiceEdit($id, $data);
return success('SUCCESS');
}
}

View File

@ -0,0 +1,177 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\adminapi\controller\delivery_store;
use addon\shop\app\service\admin\delivery_store\DeliveryStoreService;
use app\upgrade\v158\Upgrade;
use core\base\BaseAdminController;
use think\Response;
/**
* 提货点控制器
* Class DeliveryStore
* @package addon\shop\app\adminapi\controller\delivery_store
*/
class DeliveryStore extends BaseAdminController
{
/**
* 获取展示用信息
* @description 获取添加/编辑展示用信息
* @return Response
*/
public function init()
{
return success(( new DeliveryStoreService() )->getInitInfo());
}
/**
* 获取提货点列表
* @description 查看提货点列表详情
* @return \think\Response
*/
public function lists()
{
$data = $this->request->params([
['store_name', ''],
['pick_up_type', ''],//提货类型
['create_time', [] ]
]);
return success(( new DeliveryStoreService() )->getPage($data));
}
/**
* 提货点详情
* @param int $id
* @return \think\Response
*/
public function info(int $id)
{
return success(( new DeliveryStoreService() )->getInfo($id));
}
/**
* 添加提货点
* @description 添加提货点
* @return \think\Response
*/
public function add()
{
$data = $this->request->params([
['pick_up_type', ''],//提货类型
['store_name', ''],//门店名称
['store_logo', ''],//门店logo
['contact_name', ''],
['store_mobile', ''],
[ 'province_id',0 ],
[ 'city_id',0 ],
[ 'district_id',0 ],
['address', ''],
['full_address', ''],
['longitude', ''],
['latitude', ''],
['trade_time', ''],
['time_is_open', ''],
['time_week', [] ],//周数组
['time_interval', ''],//间隔时间段 单位分钟
['trade_time_json', [] ],//营业时间
[ 'area', [] ],//配送区域
['status', 0 ]
]);
$this->validate($data, 'addon\shop\app\validate\delivery\Store.add');
$id = ( new DeliveryStoreService() )->add($data);
return success('ADD_SUCCESS', [ 'id' => $id ]);
}
/**
* 提货点编辑
* @description 编辑提货点
* @param int $id 提货点id
* @return \think\Response
*/
public function edit(int $id)
{
$data = $this->request->params([
['pick_up_type', ''],//提货类型
['store_name', ''],//门店名称
['store_logo', ''],//门店logo
['contact_name', ''],
['store_mobile', ''],
[ 'province_id',0 ],
[ 'city_id',0 ],
[ 'district_id',0 ],
['address', ''],
['full_address', ''],
['longitude', ''],
['latitude', ''],
['trade_time', ''],
['time_is_open', ''],
['time_week', [] ],//周数组
['time_interval', ''],//间隔时间段 单位分钟
['trade_time_json', [] ],//营业时间
[ 'area', [] ],//配送区域
['status', 0 ]
]);
$this->validate($data, 'addon\shop\app\validate\delivery\Store.edit');
( new DeliveryStoreService() )->edit($id, $data);
return success('EDIT_SUCCESS');
}
/**
* 提货点删除
* @description 删除提货点
* @param int $id 提货点id
* @return \think\Response
*/
public function del(int $id)
{
( new DeliveryStoreService() )->del($id);
return success('DELETE_SUCCESS');
}
/**
* @description 查看提货点列表-全部
* @return \think\Response
*/
public function getList()
{
$data = $this->request->params([
['keyword', ''],
['pick_up_type', '' ]
]);
return success(( new DeliveryStoreService() )->getList($data));
}
/**
* @description 提货点提货类型
* @return \think\Response
*/
public function getPickUpType()
{
return success(( new DeliveryStoreService() )->getPickUpType());
}
/**
* 修改社区分类状态
* @return \think\Response
*/
public function modifyStatus()
{
$data = $this->request->params([
[ 'store_id', '' ],
[ 'status', '' ],
]);
( new DeliveryStoreService() )->modifyStatus($data);
return success('SUCCESS');
}
}

View File

@ -85,6 +85,8 @@ class Goods extends BaseAdminController
["sub_title", ""],
["goods_type", ""],
["goods_image", ""],
["goods_image_width", ""],
["goods_image_height", ""],
["goods_video", ""],
["goods_category", ''],
["brand_id", 0],
@ -154,6 +156,8 @@ class Goods extends BaseAdminController
["sub_title", ""],
["goods_type", ""],
["goods_image", ""],
["goods_image_width", ""],
["goods_image_height", ""],
["goods_video", ""],
["goods_category", ''],
["brand_id", 0],

View File

@ -47,6 +47,8 @@ class VirtualGoods extends BaseAdminController
[ "sub_title", "" ],
[ "goods_type", "" ],
[ "goods_image", "" ],
["goods_image_width", ""],
["goods_image_height", ""],
[ "goods_video", "" ],
[ "goods_category", '' ],
[ "brand_id", 0 ],
@ -113,6 +115,8 @@ class VirtualGoods extends BaseAdminController
[ "sub_title", "" ],
[ "goods_type", "" ],
[ "goods_image", "" ],
["goods_image_width", ""],
["goods_image_height", ""],
[ "goods_video", "" ],
[ "goods_category", '' ],
[ "brand_id", 0 ],

View File

@ -0,0 +1,49 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\adminapi\controller\local_delivery\config;
use addon\shop\app\service\admin\local_delivery\config\ConfigService;
use core\base\BaseAdminController;
use think\Response;
/**
* 配送配置
* Class Config
* @package addon\shop\app\adminapi\controller\local_delivery\config
*/
class Config extends BaseAdminController
{
/**
* 设置配置
* @return Response
*/
public function setConfig(): Response
{
$data = $this->request->params([
['is_show_polyline',0],//是否显示配送轨迹
['is_local_delivery_store_select',0],//是否开启门店选择
]);
(new ConfigService())->setConfig($data);
return success('SUCCESS');
}
/**
* 获取配置
* @return Response
*/
public function getConfig(): Response
{
return success((new ConfigService())->getConfig());
}
}

View File

@ -0,0 +1,65 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\adminapi\controller\local_delivery\config;
use addon\shop\app\service\admin\local_delivery\config\LocalService;
use core\base\BaseAdminController;
/**
* 同城配送
* @description 同城配送
* Class Store
* @package addon\shop\app\adminapi\controller\delivery
*/
class Local extends BaseAdminController
{
/**
* @description 查看设置
* @return \think\Response
*/
public function getLocal()
{
return success(data: (new LocalService())->getLocal());
}
/**
* @description 设置配置
* @return \think\Response
*/
public function setLocal()
{
$data = $this->request->params([
['fee_type', ''],
['base_dist', 0],
['base_price', 0],
['grad_dist', 0],
['grad_price', 0],
['weight_start', 0],
['weight_unit', 0],
['weight_price', 0],
['time_is_open', 0],
['time_type', 0],
['time_week', ''],
['time_interval', 30],
['advance_day', 0],
['most_day', 7],
['start_time', 0],
['end_time', 0],
['delivery_time', ''],
['area_data', []],
]);
return success(data: (new LocalService())->setLocal($data));
}
}

View File

@ -0,0 +1,120 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\adminapi\controller\local_delivery\order;
use addon\shop\app\service\admin\local_delivery\order\LocalDeliveryOrderService;
use core\base\BaseAdminController;
use think\Response;
/**
* 本地配送订单控制器
* Class LocalDeliveryOrder
* @package addon\shop\app\adminapi\controller\local_delivery\order
*/
class LocalDeliveryOrder extends BaseAdminController
{
/**
* 本地配送订单分页列表
* @return Response
*/
public function pages(): Response
{
$data = $this->request->params([
['delivery_no', ''],
['trade_no', ''],
['status', ''],
['create_time', []],
]);
return success(( new LocalDeliveryOrderService() )->getPage($data));
}
/**
* 本地配送订单详情
* @param int $id
* @return Response
*/
public function info(int $id): Response
{
return success(( new LocalDeliveryOrderService() )->getInfo($id));
}
/**
* 取消订单
* @param int $id
* @return Response
*/
public function closeOrder(int $id): Response
{
$data = $this->request->params([
['cancel_reason_id', ''],
['cancel_reason', '']
]);
$res = ( new LocalDeliveryOrderService() )->closeOrder($id, $data);
if ($res) {
return success('CANCEL_SUCCESS');
} else {
return fail('FAIL');
}
}
/**
* 完成订单
* @param int $id
* @return Response
*/
public function finishOrder(int $id): Response
{
$res = ( new LocalDeliveryOrderService() )->finishOrder($id);
if ($res) {
return success('SUCCESS');
} else {
return fail('FAIL');
}
}
/**
* 同步订单信息
* @param int $id
* @return Response
*/
public function syncOrder(int $id): Response
{
$res = ( new LocalDeliveryOrderService() )->syncOrder($id);
if ($res) {
return success('SUCCESS');
} else {
return fail('FAIL');
}
}
/**
* 订单取消原因
* @return Response
*/
public function getCancelReasonList(): Response
{
$data = $this->request->params([
['service', '']
]);
return success(( new LocalDeliveryOrderService() )->getCancelReasonList($data['service']));
}
/**
* 获取订单状态列表
* @return Response
*/
public function getOrderStatusList(): Response
{
return success((new LocalDeliveryOrderService())->getOrderStatusList());
}
}

View File

@ -0,0 +1,79 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\adminapi\controller\local_delivery\service;
use addon\shop\app\dict\local_delivery\LocalDeliveryDict;
use addon\shop\app\service\admin\local_delivery\service\LocalDeliveryServiceService;
use core\base\BaseAdminController;
use core\exception\AdminException;
use think\Response;
/**
* 配送服务商
* Class LocalDeliveryService
* @package addon\shop\app\adminapi\controller\local_delivery\service
*/
class LocalDeliveryService extends BaseAdminController
{
/**
* 获取配送服务商列表
*/
public function lists()
{
$res = (new LocalDeliveryServiceService())->getList();
return success($res);
}
/**
* 设置配置
* @return Response
*/
public function setConfig($delivery_type)
{
//参数获取
$delivery_type_list = LocalDeliveryDict::getType();
if (!array_key_exists($delivery_type, $delivery_type_list)) throw new AdminException('LOCAL_DELIVERY_TYPE_NOT_EXIST');
//数据验证
$data = [
['is_use', 0]
];
foreach ($delivery_type_list[$delivery_type]['params'] as $k_param => $v_param) {
$data[] = [$k_param, ''];
}
$request_data = $this->request->params($data);
(new LocalDeliveryServiceService())->setConfig($delivery_type, $request_data);
return success('SUCCESS');
}
/**
* 获取配置
* @return Response
*/
public function getConfig($delivery_type)
{
return success((new LocalDeliveryServiceService())->getConfig($delivery_type,true));
}
/**
* 获取已启用配送服务商列表
* @return Response
*/
public function getInUseList()
{
$data = $this->request->params([
['store_id', 0],
]);
return success((new LocalDeliveryServiceService())->getInUseList($data));
}
}

View File

@ -122,6 +122,9 @@ class Order extends BaseAdminController
['electronic_sheet_id', 0], // 电子面单
['local_deliver_id', 0],//配送员
['remark', ''],//配送员
['local_delivery_type', ''],
['goods_weight', 0],
['store_id', ''],//提货点id
]);
return success("DELIVERY_SUCCESS", (new OrderDeliveryService())->delivery($data));
}
@ -324,6 +327,35 @@ class Order extends BaseAdminController
return success(data: OrderBatchDeliveryDict::getStatus());
}
/**
* 获取已选商品总重量
* @return \think\Response
*/
public function getSelectOrderGoodsWeight()
{
$data = $this->request->params([
[ 'order_goods_ids', [] ], // 已选订单项id集合
]);
return success(data:( new OrderService() )->getSelectOrderGoodsWeight($data));
}
/**
* 配送费用计算
*/
public function getDeliveryFee()
{
$data = $this->request->params([
['order_id', 0],
['order_goods_ids', []],
['delivery_type', ''],
['delivery_way', ''],
['local_delivery_type', ''],
['goods_weight', 0],
['store_id', 0],
]);
return success(data:( new OrderDeliveryService() )->getDeliveryFee($data));
}
/**
* @description 删除订单
* @return Response

View File

@ -0,0 +1,109 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\adminapi\controller\shop_delivery\deliver;
use addon\shop\app\service\admin\shop_delivery\deliver\DeliverService;
use core\base\BaseAdminController;
use think\Response;
/**
* 配送员
* @description 配送员
* Class Deliver
* @package addon\shop\app\adminapi\controller\shop_delivery\deliver
*/
class Deliver extends BaseAdminController
{
/**
* 获取配送员分页列表
* @description 查看配送员列表
* @return \think\Response
*/
public function pages()
{
$data = $this->request->params([
['deliver_name', ''],
['deliver_mobile', '']
]);
return success(( new DeliverService() )->getPage($data));
}
/**
* 获取配送员列表
* @description 查看配送员列表
* @return \think\Response
*/
public function lists()
{
$data = $this->request->params([
['deliver_name', ''],
['deliver_mobile', '']
]);
return success(( new DeliverService() )->getList($data));
}
/**
* 配送员详情
* @description 查看配送员详情
* @param int $id
* @return \think\Response
*/
public function info(int $id)
{
return success(( new DeliverService() )->getInfo($id));
}
/**
* 添加配送员
* @description 添加配送员
* @return \think\Response
*/
public function add()
{
$data = $this->request->params([
['deliver_name', ''],
['deliver_mobile', ''],
]);
$id = ( new DeliverService() )->add($data);
return success('ADD_SUCCESS', [ 'id' => $id ]);
}
/**
* 配送员编辑
* @description 编辑配送员
* @param int $id 自提门店id
* @return \think\Response
*/
public function edit($id)
{
$data = $this->request->params([
['deliver_name', ''],
['deliver_mobile', ''],
]);
( new DeliverService() )->edit($id, $data);
return success('EDIT_SUCCESS');
}
/**
* 配送员删除
* @description 删除配送员
* @param int $id
* @return Response
*/
public function del(int $id)
{
( new DeliverService() )->del($id);
return success('DELETE_SUCCESS');
}
}

View File

@ -0,0 +1,92 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\adminapi\controller\shop_delivery\order;
use addon\shop\app\service\admin\shop_delivery\order\ShopDeliveryOrderService;
use core\base\BaseAdminController;
use think\Response;
/**
* 商家配送订单控制器
* Class ShopDeliveryOrder
* @package addon\shop\app\adminapi\controller\shop_delivery\order
*/
class ShopDeliveryOrder extends BaseAdminController
{
/**
* 商家配送订单分页列表
* @return Response
*/
public function pages(): Response
{
$data = $this->request->params([
['delivery_no', ''],
['trade_no', ''],
['status', ''],
['create_time', []],
]);
return success(( new ShopDeliveryOrderService() )->getPage($data));
}
/**
* 商家配送订单详情
* @param int $id
* @return Response
*/
public function info(int $id): Response
{
return success(( new ShopDeliveryOrderService() )->getInfo($id));
}
/**
* 取消订单
* @param int $id
* @return Response
*/
public function closeOrder(int $id): Response
{
$data = $this->request->params([
['cancel_reason', '']
]);
$res = ( new ShopDeliveryOrderService() )->closeOrder($id, $data);
if ($res) {
return success('CANCEL_SUCCESS');
} else {
return fail('FAIL');
}
}
/**
* 完成订单
* @param int $id
* @return Response
*/
public function finishOrder(int $id): Response
{
$res = ( new ShopDeliveryOrderService() )->finishOrder($id);
if ($res) {
return success('SUCCESS');
} else {
return fail('FAIL');
}
}
/**
* 获取订单状态列表
* @return Response
*/
public function getOrderStatusList(): Response
{
return success((new ShopDeliveryOrderService())->getOrderStatusList());
}
}

View File

@ -14,9 +14,6 @@ use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog;
use think\facade\Route;
// 第三方配送
Route::any('delivery/third/callback/:type/:order_no', 'addon\shop\app\adminapi\controller\delivery\Local@thirdDeliveryCallback');
/**
* 商城系统
*/
@ -65,51 +62,135 @@ Route::group('shop', function () {
//运费模版 删除
Route::delete('shipping/template/:template_id', 'addon\shop\app\adminapi\controller\delivery\ShippingTemplate@del');
//自提门店列表(分页)
Route::get('delivery/store', 'addon\shop\app\adminapi\controller\delivery\Store@lists');
/************************************************** 提货点 *****************************************************/
//提货点列表(分页)
Route::get('delivery/store', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryStore@lists');
Route::get('delivery/store/init', 'addon\shop\app\adminapi\controller\delivery\Store@init');
//提货点初始化数据
Route::get('delivery/store/init', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryStore@init');
//自提门店列表(不分页)
Route::get('delivery/store/list', 'addon\shop\app\adminapi\controller\delivery\Store@getList');
//提货点列表(不分页)
Route::get('delivery/store/list', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryStore@getList');
//自提门店详情
Route::get('delivery/store/:id', 'addon\shop\app\adminapi\controller\delivery\Store@info');
//提货点详情
Route::get('delivery/store/:id', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryStore@info');
//添加自提门店
Route::post('delivery/store', 'addon\shop\app\adminapi\controller\delivery\Store@add');
//添加提货点
Route::post('delivery/store', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryStore@add');
//编辑自提门店
Route::put('delivery/store/:id', 'addon\shop\app\adminapi\controller\delivery\Store@edit');
//编辑提货点
Route::put('delivery/store/:id', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryStore@edit');
//删除自提门店
Route::delete('delivery/store/:id', 'addon\shop\app\adminapi\controller\delivery\Store@del');
//删除提货点
Route::delete('delivery/store/:id', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryStore@del');
//提货点提货类型
Route::get('delivery/store/pick_up_type', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryStore@getPickUpType');
//修改提货点状态
Route::put('delivery/store/modify_status', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryStore@modifyStatus');
/************************************************** 配送服务商 *****************************************************/
//获取配送服务商列表
Route::get('delivery_store/delivery_service_list/:id', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryService@getDeliveryServiceList');
//配送门店开通配送服务
Route::put('delivery_store/delivery_shop_open/:id', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryService@deliveryServiceOpen');
//配送门店品类修改
Route::put('delivery_store/delivery_shop_edit/:id', 'addon\shop\app\adminapi\controller\delivery_store\DeliveryService@deliveryServiceEdit');
/************************************************** 配送设置 *****************************************************/
//物流配置
Route::get('delivery/deliveryList', 'addon\shop\app\adminapi\controller\delivery\Delivery@getDeliveryList');
Route::put('delivery/setConfig', 'addon\shop\app\adminapi\controller\delivery\Delivery@setDeliveryConfig');
//配送员列表
Route::get('delivery/staff', 'addon\shop\app\adminapi\controller\delivery\Delivery@lists');
//获取配送方式
Route::get('delivery/delivery_type', 'addon\shop\app\adminapi\controller\delivery\Delivery@getDeliveryType');
//配送员详情
Route::get('delivery/staff/:id', 'addon\shop\app\adminapi\controller\delivery\Delivery@info');
//添加配送员
Route::post('delivery/staff', 'addon\shop\app\adminapi\controller\delivery\Delivery@add');
//编辑配送员
Route::put('delivery/staff/:id', 'addon\shop\app\adminapi\controller\delivery\Delivery@edit');
//删除配送员
Route::delete('delivery/staff/:id', 'addon\shop\app\adminapi\controller\delivery\Delivery@del');
/************************************************** 同城配送 *******************************************************/
// 获取同城配送设置
Route::get('local', 'addon\shop\app\adminapi\controller\delivery\Local@getLocal');
Route::get('local_delivery/config', 'addon\shop\app\adminapi\controller\local_delivery\config\Local@getLocal');
// 设置同城配送
Route::put('local', 'addon\shop\app\adminapi\controller\delivery\Local@setLocal');
Route::get('third/init', 'addon\shop\app\adminapi\controller\delivery\Local@getThirdPartyInit');
Route::put('local_delivery/config', 'addon\shop\app\adminapi\controller\local_delivery\config\Local@setLocal');
// 获取同城配送基础配置
Route::get('local_delivery/base_config', 'addon\shop\app\adminapi\controller\local_delivery\config\Config@getConfig');
// 设置同城配送基础配置
Route::put('local_delivery/base_config', 'addon\shop\app\adminapi\controller\local_delivery\config\Config@setConfig');
//获取已启用配送服务商列表
Route::get('local_delivery/list', 'addon\shop\app\adminapi\controller\local_delivery\service\LocalDeliveryService@getInUseList');
//同城配送服务商列表
Route::get('local_delivery/service', 'addon\shop\app\adminapi\controller\local_delivery\service\LocalDeliveryService@lists');
//同城配送服务商配置
Route::get('local_delivery/service/:delivery_type', 'addon\shop\app\adminapi\controller\local_delivery\service\LocalDeliveryService@getConfig');
//同城配送服务商配置修改
Route::put('local_delivery/service/:delivery_type', 'addon\shop\app\adminapi\controller\local_delivery\service\LocalDeliveryService@setConfig');
// 同城配送订单
Route::get('local_delivery/order', 'addon\shop\app\adminapi\controller\local_delivery\order\LocalDeliveryOrder@pages');
// 同城配送订单详情
Route::get('local_delivery/order/:id', 'addon\shop\app\adminapi\controller\local_delivery\order\LocalDeliveryOrder@info');
// 同城配送订单同步
Route::put('local_delivery/order/sync/:id', 'addon\shop\app\adminapi\controller\local_delivery\order\LocalDeliveryOrder@syncOrder');
// 同城配送订单取消原因
Route::get('local_delivery/order/cancel_reason', 'addon\shop\app\adminapi\controller\local_delivery\order\LocalDeliveryOrder@getCancelReasonList');
// 同城配送订单取消
Route::put('local_delivery/order/cancel/:id', 'addon\shop\app\adminapi\controller\local_delivery\order\LocalDeliveryOrder@closeOrder');
// 同城配送订单完成
Route::put('local_delivery/order/finish/:id', 'addon\shop\app\adminapi\controller\local_delivery\order\LocalDeliveryOrder@finishOrder');
// 同城配送订单状态列表
Route::get('local_delivery/order/status', 'addon\shop\app\adminapi\controller\local_delivery\order\LocalDeliveryOrder@getOrderStatusList');
/************************************************** 商家配送 *******************************************************/
//配送员分页列表
Route::get('shop_delivery/staff', 'addon\shop\app\adminapi\controller\shop_delivery\deliver\Deliver@pages');
//配送员列表
Route::get('shop_delivery/staff/list', 'addon\shop\app\adminapi\controller\shop_delivery\deliver\Deliver@lists');
//配送员详情
Route::get('shop_delivery/staff/:id', 'addon\shop\app\adminapi\controller\shop_delivery\deliver\Deliver@info');
//添加配送员
Route::post('shop_delivery/staff', 'addon\shop\app\adminapi\controller\shop_delivery\deliver\Deliver@add');
//编辑配送员
Route::put('shop_delivery/staff/:id', 'addon\shop\app\adminapi\controller\shop_delivery\deliver\Deliver@edit');
//删除配送员
Route::delete('shop_delivery/staff/:id', 'addon\shop\app\adminapi\controller\shop_delivery\deliver\Deliver@del');
// 商家配送订单
Route::get('shop_delivery/order', 'addon\shop\app\adminapi\controller\shop_delivery\order\ShopDeliveryOrder@pages');
// 商家配送订单详情
Route::get('shop_delivery/order/:id', 'addon\shop\app\adminapi\controller\shop_delivery\order\ShopDeliveryOrder@info');
// 商家配送订单取消
Route::put('shop_delivery/order/cancel/:id', 'addon\shop\app\adminapi\controller\shop_delivery\order\ShopDeliveryOrder@closeOrder');
// 商家配送订单完成
Route::put('shop_delivery/order/finish/:id', 'addon\shop\app\adminapi\controller\shop_delivery\order\ShopDeliveryOrder@finishOrder');
// 商家配送订单状态列表
Route::get('shop_delivery/order/status', 'addon\shop\app\adminapi\controller\shop_delivery\order\ShopDeliveryOrder@getOrderStatusList');
// Route::get('third/init', 'addon\shop\app\adminapi\controller\delivery\Local@getThirdPartyInit');
/************************************************** 接口管理 *******************************************************/
@ -414,6 +495,12 @@ Route::group('shop', function () {
//获取订单配送方式
Route::get('order/delivery_type', 'addon\shop\app\adminapi\controller\order\Order@getDeliveryType');
//获取已选订单项总重量
Route::get('order/select/weight', 'addon\shop\app\adminapi\controller\order\Order@getSelectOrderGoodsWeight');
//同城配送费用计算
Route::get('order/delivery/fee', 'addon\shop\app\adminapi\controller\order\Order@getDeliveryFee');
//商家留言
Route::put('order/shop_remark', 'addon\shop\app\adminapi\controller\order\Order@setShopRemark');

View File

@ -0,0 +1,47 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\api\controller\delivery;
use addon\shop\app\service\api\delivery\OrderService;
use addon\shop\app\service\core\local_delivery\CoreLocalDeliveryOrderNotifyService;
use core\base\BaseApiController;
use think\Response;
class Order extends BaseApiController
{
/**
* 订单状态回调
* @param $type
* @param $order_no
* @param $action
* @return Response
*/
public function notify($type, $order_no, $action)
{
$params = $this->request->all();
$params['type'] = $type;
$params['order_no'] = $order_no;
return (new CoreLocalDeliveryOrderNotifyService())->notify($type, $order_no, $action, $params);
}
/**
* 获取配送轨迹
* @return Response
*/
public function getTrackOfLocalDeliveryOrder()
{
$data = $this->request->params([
[ 'trade_no', '' ],
]);
return success(( new OrderService() )->getTrackOfLocalDeliveryOrder($data));
}
}

View File

@ -49,6 +49,7 @@ class OrderCreate extends BaseApiController
// ['invoice', []],//发票
['extend_data', []],// 扩展数据
['impulse_buy_goods', []], // 顺买商品信息
['position', []],//用户位置信息
]);
return success('SUCCESS', ( new OrderCreateService() )->calculate($data));

View File

@ -14,7 +14,8 @@ use app\api\middleware\ApiCheckToken;
use app\api\middleware\ApiLog;
use think\facade\Route;
// 第三方配送
Route::any('delivery/notify/:type/:order_no/:action', 'addon\shop\app\api\controller\delivery\Order@notify');
/**
* 商城系统
*/
@ -204,6 +205,9 @@ Route::group('shop', function () {
Route::get('order_create/store', 'addon\shop\app\api\controller\order\OrderCreate@getStore');
Route::get('order_create/local', 'addon\shop\app\api\controller\order\OrderCreate@getLocalDeliveryConfig');
// 获取同城配送轨迹
Route::get('delivery/track_of_local', 'addon\shop\app\api\controller\delivery\Order@getTrackOfLocalDeliveryOrder');
//获取订单确认数据
Route::get('confirm', 'addon\shop\app\api\controller\order\OrderCreate@confirm');

View File

@ -14,6 +14,9 @@ namespace addon\shop\app\dict\delivery;
class ElectronicSheetDict
{
/**
* 获取邮费支付方式类型
* @param $type

View File

@ -0,0 +1,44 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\dict\delivery_store;
/**
* 提货点枚举类
* Class LocalDeliveryDict
* @package app\dict\sys
*/
class DeliveryStoreDict
{
//同城配送
const LOCAL_DELIVERY = 'local_delivery';
//门店自提
const STORE = 'store';
/**
* 获取提货类型
* @param string $type
* @return array|string
*/
public static function getPickUpType($type = '')
{
$pick_up_type = [
self::LOCAL_DELIVERY => get_lang('dict_shop_delivery_store_pick_up_type.local_delivery'),
self::STORE => get_lang('dict_shop_delivery_store_pick_up_type.store'),
];
if (!empty($type)) {
return $pick_up_type[$type] ?? '';
}
return $pick_up_type;
}
}

View File

@ -25,6 +25,7 @@ return [
"fontWeight" => 'normal',
"isShow" => true
],
"mode" => "aspectFill",
"priceStyle" => [
"color" => "#FF4142",
"control" => true,
@ -126,6 +127,7 @@ return [
'value' => [
'style' => 'style-2',
'num' => 6,
"mode" => "aspectFill",
"sortWay" => "default", // 排序方式default综合sale_num销量price价格
"headStyle" => "style-1",
"aroundRadius" => 25,
@ -237,8 +239,8 @@ return [
"componentStartBgColor" => '', // 组件背景颜色(开始)
"componentEndBgColor" => '', // 组件背景颜色(结束)
"componentGradientAngle" => 'to bottom', // 渐变角度上下to bottom、左右to right
"topRounded" => 0, // 组件上圆角
"bottomRounded" => 0, // 组件下圆角
"topRounded" => 12, // 组件上圆角
"bottomRounded" => 12, // 组件下圆角
"elementBgColor" => '', // 元素背景颜色
"topElementRounded" => 0,// 元素上圆角
"bottomElementRounded" => 0, // 元素下圆角
@ -308,6 +310,7 @@ return [
'sort' => 10018,
'value' => [
'style' => 'style-2',
"mode" => "aspectFill",
'source' => 'all',
'num' => 10,
'goods_category' => '',
@ -365,6 +368,7 @@ return [
"priceStyle" => [
"mainColor" => "#333333",
],
"mode" => "aspectFill",
'source' => 'all',
"goods_ids" => [],
'list' => [
@ -465,6 +469,7 @@ return [
'uses' => 0,
'sort' => 10019,
'value' => [
"mode" => "aspectFill",
"titleStyle" => [
'title' => '风格1',
'value' => 'style-1'
@ -553,6 +558,7 @@ return [
'uses' => 1,
'sort' => 10020,
'value' => [
"mode" => "aspectFill",
"style" => [
'title' => '风格1',
'value' => 'style-1'
@ -611,6 +617,7 @@ return [
'uses' => 0,
'sort' => 10021,
'value' => [
"mode" => "aspectFill",
"list" => [
[
'bgUrl' => 'addon/shop/rank/rank_bg_01.jpg', // 榜单背景图
@ -682,7 +689,81 @@ return [
"both" => 10 // 左右边距
]
]
]
],
'ShopGoodsHot' => [
'title' => '爆款商品',
'icon' => 'iconfont iconjifenshangpinpc',
'path' => 'edit-shop-goods-hot',
'support_page' => [],
'uses' => 0,
'sort' => 10022,
'value' => [
"mode" => "aspectFill",
'titleStyle' => [
'text' => '大牌直降',
'textColor' => '#FFFFFF',
"fontWeight" => 'bold',
"fontSize" => 18,
'imgUrl' => '',
'way' => 'text', // 展示方式text文本img图片
"link" => [
"name" => ""
]
],
'subTitleStyle' => [
'text' => '7月10日-29日',
'textColor' => '#ffffff',
'bgColor' => '#ff737a',
"rounded" => 6,
"fontSize" => 12
],
'btnStyle' => [
"text" => '立即抢购',
'textColor' => '#ee695c',
"startBgColor" => "",
"endBgColor" => "",
"gradientType" => "radial",
"fontSize" => 12,
"link" => [
"name" => ""
]
],
'goodsStyle' => [
'bgColor' => '#ffffff',
'labelText' => '大牌直降',
'labelSize' => 10,
'labelColor' => '#ffffff',
'labelBgColor' => '#FF0000',
"rounded" => 8,
"imgRounded" => 7,
'source' => 'all', // 商品来源方式
'num' => 10,
"goodsIds" => []
]
],
// 组件属性
'template' => [
"textColor" => "#303133", // 文字颜色
'pageStartBgColor' => '#FF0000', // 底部背景颜色(开始)
'pageEndBgColor' => '', // 底部背景颜色(结束)
'pageGradientAngle' => 'to bottom', // 渐变角度从上到下to bottom、从左到右to right
'componentBgUrl' => '', // 组件背景图片
'componentBgAlpha' => 2, // 组件背景图片的透明度0~10
"componentStartBgColor" => '', // 组件背景颜色(开始)
"componentEndBgColor" => '', // 组件背景颜色(结束)
"componentGradientAngle" => 'to bottom', // 渐变角度上下to bottom、左右to right
"topRounded" => 0, // 组件上圆角
"bottomRounded" => 0, // 组件下圆角
"elementBgColor" => '', // 元素背景颜色
"topElementRounded" => 0,// 元素上圆角
"bottomElementRounded" => 0, // 元素下圆角
"margin" => [
"top" => 12, // 上边距
"bottom" => 12, // 下边距
"both" => 10 // 左右边距
],
]
],
]
],
'SHOP_GOODS_DETAIL_COMPONENT' => [

View File

@ -967,6 +967,7 @@ return [
"id" => "2ezdal8ps1og",
"componentName" => "ShopGoodsRecommend",
"componentTitle" => "商品推荐",
"mode" => "aspectFill",
"ignore" => [],
"priceStyle" => [
"mainColor" => "#333333"
@ -1441,6 +1442,7 @@ return [
"id" => "2hjc1q7f7q80",
"componentName" => "ShopNewcomer",
"componentTitle" => "新人专享",
"mode" => "aspectFill",
"ignore" => [],
"style" => [
"title" => "风格1",
@ -1496,6 +1498,7 @@ return [
"uses" => 0,
"id" => "2mxsu6pbqpu0",
"componentName" => "SingleRecommend",
"mode" => "aspectFill",
"componentTitle" => "精选推荐",
"ignore" => [],
"titleStyle" => [
@ -1631,6 +1634,7 @@ return [
"id" => "37adfqtqe080",
"componentName" => "ManyGoodsList",
"componentTitle" => "多商品组",
"mode" => "aspectFill",
"ignore" => [
"componentBgUrl"
],
@ -2788,6 +2792,7 @@ return [
"id" => "17uao387qdds",
"componentName" => "GoodsList",
"componentTitle" => "商品列表",
"mode" => "aspectFill",
"ignore" => [],
"style" => "style-3",
"source" => "all",
@ -2939,6 +2944,7 @@ return [
"id" => "2u96wyrm79u0",
"componentName" => "GoodsList",
"componentTitle" => "商品列表",
"mode" => "aspectFill",
"ignore" => [],
"style" => "style-1",
"source" => "all",
@ -3059,6 +3065,7 @@ return [
"id" => "4y7mw7j2uko0",
"componentName" => "GoodsList",
"componentTitle" => "商品列表",
"mode" => "aspectFill",
"ignore" => [],
"style" => "style-2",
"source" => "all",
@ -3126,6 +3133,7 @@ return [
"id" => "65lv65b38ig0",
"componentName" => "ManyGoodsList",
"componentTitle" => "多商品组",
"mode" => "aspectFill",
"ignore" => ['componentBgUrl'],
"style" => "style-2",
"num" => 6,
@ -3767,6 +3775,7 @@ return [
"id" => "50pj7dmvbvo0",
"componentName" => "GoodsList",
"componentTitle" => "商品列表",
"mode" => "aspectFill",
"ignore" => [],
'style' => 'style-1',
"source" => "all",
@ -5182,6 +5191,7 @@ return [
"id" => "76wf7oom00o0",
"componentName" => "ShopExchangeGoods",
"componentTitle" => "积分商品",
"mode" => "aspectFill",
"path" => "edit-shop-exchange-goods",
"uses" => 0,
"ignore" => [],

View File

@ -0,0 +1,56 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\dict\local_delivery;
/**
* 同城配送枚举类
* Class LocalDeliveryDict
* @package addon\shop\app\dict\local_delivery
*/
class LocalDeliveryDict
{
//商家配送
const MERCHANT = 'merchant';
public static function getType($type = '')
{
$list = [
self::MERCHANT => [
'name' => '商家配送',
'key' => self::MERCHANT,
//配置参数
'params' => [],
'component' => '',
'business_list' => '',
'extend_data' => [],
//商家配送标识
'is_merchant' => 1
],
];
$extend = event('LocalDeliveryType');
$type_list = array_merge($list, ...$extend);
if ($type == '') return $type_list;
return $type_list[$type]['name'] ?? '';
}
//门店开通状态
const OPEN_PASS = 1;//通过
const OPEN_REFUND = 2;//未通过
//门店编辑状态
const EDIT_PASS = 1;//编辑审核通过
const EDIT_REFUND = 2;//编辑审核驳回
}

View File

@ -0,0 +1,30 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\dict\local_delivery;
/**
* 配送订单操作前置状态枚举类
* Class LocalDeliveryDict
* @package app\dict\sys
*/
class LocalDeliveryPreStatusDict
{
public static function getStatus(string $service = '')
{
$list = [];
$extend = array_filter(event('DeliveryOrderPreStatus', ['service' => $service]));
return array_merge($list, ...$extend);
}
}

View File

@ -0,0 +1,63 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\dict\local_delivery;
/**
*同城配送相关枚举类
*/
class LocalDeliveryStatusDict
{
const WAIT_RELEASE = -100; // 预约待发布
// 订单状态常量定义
const ORDER_ACCEPTED = 0; // 商家已接单
const PENDING_ACCEPTANCE = 1; // 待接单
const PENDING_PICKUP = 2; // 待取货
const RIDER_ARRIVED = 3; // 骑士到店
const IN_DELIVERY = 4; // 配送中
const COMPLETED = 5; // 已完成
const DISPATCH_ORDER = 6; // 指派单(已追加待接单)
const RETURN_IN_PROGRESS = 7; // 妥投异常之物品返回中
const RETURN_COMPLETED = 8; // 妥投异常之物品返回完成
const RETURN_RIDER_ARRIVED = 9; // 售后取件单送达门店
const CREATE_ORDER_FAILED = -1; // 创建运单失败
const CANCELED = -2; // 已取消
/**
* 获取所有订单状态映射
* @return array|mixed|string
*/
public static function getStatus(string $status = '')
{
$data = [
self::WAIT_RELEASE => get_lang('dict_shop_local_delivery_status.wait_release'), //预约待发布,
self::ORDER_ACCEPTED => get_lang('dict_shop_local_delivery_status.order_accepted'), //商家已接单,
self::PENDING_ACCEPTANCE => get_lang('dict_shop_local_delivery_status.pending_acceptance'), //待接单,
self::PENDING_PICKUP => get_lang('dict_shop_local_delivery_status.pending_pickup'), //待取货,
self::RIDER_ARRIVED => get_lang('dict_shop_local_delivery_status.rider_arrived'), //骑士到店,
self::IN_DELIVERY => get_lang('dict_shop_local_delivery_status.in_delivery'), //配送中,
self::COMPLETED => get_lang('dict_shop_local_delivery_status.completed'), //已完成,
self::DISPATCH_ORDER => get_lang('dict_shop_local_delivery_status.dispatch_order'), //指派单,
self::RETURN_IN_PROGRESS => get_lang('dict_shop_local_delivery_status.return_in_progress'), //妥投异常之物品返回中,
self::RETURN_COMPLETED => get_lang('dict_shop_local_delivery_status.return_completed'), //妥投异常之物品返回完成,
self::RETURN_RIDER_ARRIVED => get_lang('dict_shop_local_delivery_status.return_rider_arrived'), //售后取件单送达门店,
self::CREATE_ORDER_FAILED => get_lang('dict_shop_local_delivery_status.create_order_failed'), //创建运单失败,
self::CANCELED => get_lang('dict_shop_local_delivery_status.canceled'), //已取消,
];
if ($status == '' && $status !== 0) {
return $data;
}
return $data[$status] ?? '';
}
}

View File

@ -0,0 +1,33 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\dict\local_delivery;
/**
*订单取消原因相关枚举类
*/
class OrderCancelReasonDict
{
/**
* 获取订单取消原因枚举列表
* @return array|mixed|string
*/
public static function getType($type = '', string $service = '')
{
$data = array_filter(event('OrderCancelReason', ['service' => $service]))[0];
if ($type == '') {
return $data;
}
return $data[$type] ?? '';
}
}

View File

@ -0,0 +1,121 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\dict\local_delivery;
/**
* 订单日志枚举类
*/
class OrderLogDict
{
const STORE = 'store';
const RIDER = 'rider';
const MEMBER = 'member';
const SYSTEM = 'system';
/**
* 获取操作人类型
* @param string $type
* @return array|array[]|string
*/
public static function getMainType(string $type = '')
{
$data = [
self::STORE => get_lang('dict_shop_local_delivery_order_log.store'),//商家
self::RIDER => get_lang('dict_shop_local_delivery_order_log.rider'),//骑手
self::MEMBER => get_lang('dict_shop_local_delivery_order_log.member'),//会员
self::SYSTEM => get_lang('dict_shop_local_delivery_order_log.system'),//系统
];
if (!$type) {
return $data;
}
return $data[$type] ?? '';
}
const ORDER_ACCEPTED = 'order_accepted';//商家已接单
const ORDER_CALL = 'order_call';//商家呼叫配送
const DISPATCH_ORDER = 'dispatch_order';//商家指派骑手
const RIDER_ACCEPTED = 'rider_accepted';//骑手已接单
const RIDER_ARRIVED = 'rider_arrived';//骑手已到店
const RIDER_PICKED_UP = 'rider_picked_up';//骑手已取货
const ORDER_DELIVERED = 'order_delivered';//订单已送达
const ORDER_CANCELED = 'order_canceled';//订单已取消
const RETURN_APPLIED = 'return_applied';//物品返回申请
const RETURN_COMPLETED = 'return_completed';//物品返回完成
/**
* 获取操作
* @return array
*/
public static function getOperate($status)
{
switch ($status) {
case LocalDeliveryStatusDict::ORDER_ACCEPTED:
return [
'operate' => self::ORDER_ACCEPTED,
'operate_desc' => get_lang('dict_shop_local_delivery_order_log.order_accepted')
];// 商家已接单
case LocalDeliveryStatusDict::PENDING_ACCEPTANCE:
return [
'operate' => self::ORDER_CALL,
'operate_desc' => get_lang('dict_shop_local_delivery_order_log.order_call')
];// 商家呼叫配送
case LocalDeliveryStatusDict::DISPATCH_ORDER:
return [
'operate' => self::DISPATCH_ORDER,
'operate_desc' => get_lang('dict_shop_local_delivery_order_log.dispatch_order')
];// 商家指派骑手
case LocalDeliveryStatusDict::PENDING_PICKUP:
return [
'operate' => self::RIDER_ACCEPTED,
'operate_desc' => get_lang('dict_shop_local_delivery_order_log.rider_accepted')
];// 骑手已接单
case LocalDeliveryStatusDict::RIDER_ARRIVED:
return [
'operate' => self::RIDER_ARRIVED,
'operate_desc' => get_lang('dict_shop_local_delivery_order_log.rider_arrived')
];// 骑手已到店
case LocalDeliveryStatusDict::IN_DELIVERY:
return [
'operate' => self::RIDER_PICKED_UP,
'operate_desc' => get_lang('dict_shop_local_delivery_order_log.rider_picked_up')
];// 骑手已取货
case LocalDeliveryStatusDict::COMPLETED:
return [
'operate' => self::ORDER_DELIVERED,
'operate_desc' => get_lang('dict_shop_local_delivery_order_log.order_delivered')
];// 订单已送达
case LocalDeliveryStatusDict::CANCELED:
return [
'operate' => self::ORDER_CANCELED,
'operate_desc' => get_lang('dict_shop_local_delivery_order_log.order_canceled')
];// 订单已取消
case LocalDeliveryStatusDict::RETURN_IN_PROGRESS:
return [
'operate' => self::RETURN_APPLIED,
'operate_desc' => get_lang('dict_shop_local_delivery_order_log.return_applied')
];// 物品返回申请
case LocalDeliveryStatusDict::RETURN_COMPLETED:
return [
'operate' => self::RETURN_COMPLETED,
'operate_desc' => get_lang('dict_shop_local_delivery_order_log.return_completed')
];// 物品返回完成
}
return [
'operate' => '',
'operate_desc' => ''
];
}
}

View File

@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\dict\local_delivery\dada;
/**
* 达达秒送枚举类
* Class DadaDeliveryDict
* @package addon\shop\app\dict\local_delivery\dada
*/
class DadaDeliveryDict
{
//达达秒送
const DADA = 'dada';
}

View File

@ -0,0 +1,72 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\dict\local_delivery\dada;
use addon\shop\app\dict\local_delivery\LocalDeliveryStatusDict;
/**
*同城三方配送相关枚举类
*/
class DadaDeliveryStatusDict
{
// 订单状态常量定义
const WAIT_RELEASE = 0; // 预约待发布
const PENDING_ACCEPTANCE = 1; // 待接单
const PENDING_PICKUP = 2; // 待取货
const RIDER_ARRIVED = 100; // 骑士到店
const IN_DELIVERY = 3; // 配送中
const COMPLETED = 4; // 已完成
const DISPATCH_ORDER = 8; // 指派单(已追加待接单)
const RETURN_IN_PROGRESS = 9; // 妥投异常之物品返回中
const RETURN_COMPLETED = 10; // 妥投异常之物品返回完成
const RETURN_RIDER_ARRIVED = 6; // 售后取件单送达门店
const CREATE_ORDER_FAILED = 1000; // 创建运单失败
const CANCELED = 5; // 已取消
/**
* 转换配送状态
* @param $status
* @return int
*/
public static function convertStatus($status)
{
switch ($status) {
case self::WAIT_RELEASE:
return LocalDeliveryStatusDict::WAIT_RELEASE;// 待接单
case self::PENDING_ACCEPTANCE:
return LocalDeliveryStatusDict::PENDING_ACCEPTANCE;// 待接单
case self::PENDING_PICKUP:
return LocalDeliveryStatusDict::PENDING_PICKUP;// 待取货
case self::RIDER_ARRIVED:
return LocalDeliveryStatusDict::RIDER_ARRIVED;// 骑士到店
case self::IN_DELIVERY:
return LocalDeliveryStatusDict::IN_DELIVERY;// 配送中
case self::COMPLETED:
return LocalDeliveryStatusDict::COMPLETED;// 已完成
case self::CANCELED:
return LocalDeliveryStatusDict::CANCELED;// 已取消
case self::DISPATCH_ORDER:
return LocalDeliveryStatusDict::DISPATCH_ORDER;// 指派单(已追加待接单)
case self::RETURN_IN_PROGRESS:
return LocalDeliveryStatusDict::RETURN_IN_PROGRESS;// 妥投异常之物品返回中
case self::RETURN_COMPLETED:
return LocalDeliveryStatusDict::RETURN_COMPLETED;// 妥投异常之物品返回完成
case self::RETURN_RIDER_ARRIVED:
return LocalDeliveryStatusDict::RETURN_RIDER_ARRIVED;// 售后取件单送达门店
case self::CREATE_ORDER_FAILED:
return LocalDeliveryStatusDict::CREATE_ORDER_FAILED;// 创建运单失败
}
return 0;
}
}

View File

@ -0,0 +1 @@
{"1":"食品小吃","2":"饮料","3":"鲜花绿植","5":"其他","8":"文印票务","9":"便利店","13":"水果生鲜","19":"同城电商","20":"医药","21":"蛋糕","24":"酒品","25":"小商品市场","26":"服装","27":"汽修零配","28":"数码家电","29":"小龙虾/烧烤","31":"超市","51":"火锅","53":"个护美妆","55":"母婴","57":"家居家纺","59":"手机","61":"家装","63":"成人用品","65":"校园","66":"高端市场"}

View File

@ -111,5 +111,24 @@ return [
//是否累增
'is_change_get' => 1,
]
],
//成长值
MemberAccountTypeDict::GROWTH => [
'shop_buy_goods' => [
//名称
'name' => '购买产品',
//是否增加
'inc' => 1,
//是否减少
'dec' => 0,
],
'shop_buy_order' => [
//名称
'name' => '完成下单',
//是否增加
'inc' => 1,
//是否减少
'dec' => 0,
],
]
];

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\dict\shop_delivery;
/**
* 商家配送配送枚举类
* Class LocalDeliveryDict
* @package app\dict\sys
*/
class ShopDeliveryDict
{
//商家配送
const MERCHANT = 'merchant';
public static function getType()
{
$system = [
self::MERCHANT => [
'name' => '商家配送',
'key' => self::MERCHANT,
//配置参数
'params' => [],
'component' => '',
'business_list' => '',
'extend_data' => [],
//商家配送标识
'is_merchant' => 1
],
];
return $system;
}
}

View File

@ -0,0 +1,58 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\dict\shop_delivery;
use addon\shop\app\dict\local_delivery\LocalDeliveryStatusDict;
/**
*同城三方配送相关枚举类
*/
class ShopDeliveryStatusDict
{
// 订单状态常量定义
const IN_DELIVERY = 4; // 配送中
const COMPLETED = 5; // 已完成
const CANCELED = -2; // 已取消
/**
* 获取商家配送订单状态映射
* @return array
*/
public static function getStatus()
{
return [
self::IN_DELIVERY => get_lang('dict_shop_local_delivery_status.in_delivery'), //配送中,
self::COMPLETED => get_lang('dict_shop_local_delivery_status.completed'), //已完成,
self::CANCELED => get_lang('dict_shop_local_delivery_status.canceled'), //已取消,
];
}
/**
* 转换配送状态
* @param $status
* @return int
*/
public static function convertStatus($status)
{
switch ($status) {
case self::IN_DELIVERY:
return LocalDeliveryStatusDict::IN_DELIVERY;// 配送中
case self::COMPLETED:
return LocalDeliveryStatusDict::COMPLETED;//已完成
case self::CANCELED:
return LocalDeliveryStatusDict::CANCELED;//已取消
}
return 0;
}
}

View File

@ -188,6 +188,21 @@ return [
//获取商品配送费用
'ThirdAddonOrderDelivery' => ['addon\shop\app\listener\third_addon\order\OrderDeliveryServiceListener'],
/***************************************************** 同城配送 start *****************************************************/
//配送方式
'LocalDeliveryType' => ['addon\shop\app\listener\local_delivery\dada\local_delivery_type\LocalDeliveryTypeListener'],
//订单取消原因
'OrderCancelReason' => ['addon\shop\app\listener\local_delivery\dada\order\OrderCancelReasonListener'],
//配送订单操作前置状态
'DeliveryOrderPreStatus' => [
'addon\shop\app\listener\local_delivery\dada\order\OrderPreStatusListener',
'addon\shop\app\listener\shop_delivery\OrderPreStatusListener'
],
//获取同城配送轨迹
'GetLocalDeliveryTrack' => ['addon\shop\app\listener\local_delivery\dada\order\GetLocalDeliveryTrack'],
/***************************************************** 同城配送 end *****************************************************/
],
'subscribe' => [
],

View File

@ -0,0 +1,27 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\job\delivery;
use addon\shop\app\service\core\delivery_store\CoreDeliveryServiceService;
use core\base\BaseJob;
/**
* 门店批量修改
*/
class DeliveryShopEditJob extends BaseJob
{
public function doJob($id)
{
(new CoreDeliveryServiceService())->batchDeliveryShopEdit($id);
return true;
}
}

View File

@ -13,10 +13,7 @@ return [
'SHOP_GOODS_LABEL_NAME_REPEAT'=>'标签名称重复',
'SHOP_GOODS_LABEL_IN_USE'=>'该标签正在使用中',
'NOT_SUPPORT_DELIVERY_TYPE' => '不支持选择的配送方式',
'NOT_SUPPORT_DELIVERY_ADDRESS' => '所选的收货地址不支持配送',
'NOT_SELECT_ADDRESS' => '需要先选择收货地址',
'NOT_SELECT_STORE' => '请选择自提点',
'NOT_NEED_DELIVERY_START_PRICE' => '未达到起送费用',
'EXIST_ORDER_NOT_DELETE_GOODS' => '存在未完成的订单,无法删除商品',
'EXIST_ORDER_NOT_EDIT_GOODS' => '存在未完成的订单,无法编辑商品',
'COUPON_STOCK_INSUFFICIENT' => '优惠券已领完',
@ -29,6 +26,7 @@ return [
'COUPON_RECEIVE_SUCCESS' => '领取成功',
'MEMBER_ALREADY_COLLECT' => '已收藏,不可重复收藏',
'CANCEL_COLLECT_SUCCESS' => '取消收藏成功',
'CANCEL_SUCCESS' => '取消成功',
'SHOP_COUPON_IS_USED_OR_EXIST' => '优惠券不存在或已使用',
'SHOP_COUPON_VALID_END_TIME_NOT_ALLOW_LT_START_TIME' => '优惠券的有效期结束时间不能小于当前时间',
@ -50,7 +48,6 @@ return [
'SHOP_VIRTUAL_ORDERS_EDIT_TAKER' => '虚拟商品订单不可以修改配送地址',
'EXPRESS_FIELD_EMPTY' => '地址信息有误',
'GOODS_NOT_DELIVERY_TYPE' => '当前商品不支持该配送方式',
'DELIVERY_TYPE_NOT_OPEN' => '商家未开启该配送方式',
'SHOP_THE_ITEM_IS_BEING_REFUNDED_OR_HAS_BEEN_REFUNDED' => '当前商品项存在退款',
@ -193,4 +190,36 @@ return [
'DISCOUNT_IS_ENABLED_NOT_EMPTY' => 'is_enabled必传',
/********************************************* 限时折扣end ****************************************************/
/************************************************************ 配送相关 *********************************************/
'DELIVERY_SUCCESS' => '发货成功',
'EXPRESS_COMPANY_NOT_EXIST' => '物流公司不存在',
'NOT_SUPPORT_DELIVERY_ADDRESS' => '所选的收货地址不支持配送',
'NOT_SELECT_ADDRESS' => '需要先选择收货地址',
'NOT_SELECT_PICK_UP_STORE' => '请选择提货点',
'NOT_SELECT_LOCAL_DELIVERY_STORE' => '请选择配送门店',
'NOT_NEED_DELIVERY_START_PRICE' => '未达到起送费用',
'LOCAL_DELIVERY_ORDER_EXIST' => '该订单已存在正在进行中的配送订单',
'ELECTRONIC_SHEET_API_EMPTY' => '未配置电子面单接口',
'ELECTRONIC_SHEET_TEMPLATE_FOUND' => '电子面单模板不存在',
'LOCAL_DELIVERY_TYPE_NOT_EXIST' => '配送服务商类型不存在',
'LOCAL_DELIVERY_OPEN_AT_LEAST_ONE_LOCAL_DELIVERY_STORE' => '同城配送已开启,至少需保留一个提货类型包含同城配送的提货点',
'STORE_OPEN_AT_LEAST_ONE_PICK_UP_STORE' => '门店自提已开启,至少需保留一个提货类型包含门店自提的提货点',
'LOCAL_DELIVERY_NOT_SET' => '同城配送未配置,请先完成配置',
'NOT_SET_LOCAL_DELIVERY_STORE' => '未配置配送门店',
'NOT_SET_PICK_UP_STORE' => '未配置提货点',
'DELIVERY_ORDER_NOT_EXIT' => '配送订单不存在',
'DELIVERY_SERVICE_NOT_SELECT' => '请选择配送平台',
'DELIVERY_STORE_NOT_EXIST' => '提货点不存在',
'DELIVERY_TYPE_NOT_OPEN' => '商家未开启该配送方式',
'GOODS_NOT_SUPPORT_LOCAL_DELIVERY' => '该商品不支持同城配送',
'GOODS_NOT_SUPPORT_EXPRESS' => '该商品不支持物流配送',
'GOODS_NOT_SUPPORT_STORE' => '该商品不支持门店自提',
'NOT_SUPPORT_DELIVERY_RANGE' => '不在配送范围内',
'SHOP_NOT_SET_LOCAL_DELIVERY_STORE' => '商家未设置配送门店',
'SHOP_NOT_SET_PICK_UP_STORE' => '商家未设置提货点',
'STORE_NOT_SET_DELIVERY_RANGE' => '该门店未设置配送范围',
'STORE_NOT_SUPPORT_LOCAL_DELIVERY' => '该门店不支持同城配送',
'STORE_NOT_SUPPORT_PICK_UP' => '该门店不支持门店自提',
'DELIVERY_WEAPP_SHIPPING_NOT_FOUND' => '小程序发货信息不存在',
];

View File

@ -37,6 +37,54 @@ return [
'90' => '90分钟',
'120' => '2小时',
],
'dict_shop_delivery_store_pick_up_type' => [
'local_delivery' => '同城配送',
'store' => '门店自提',
],
'dict_shop_local_delivery_status' => [
'wait_release' => '预约待发布',
'order_accepted' => '商家已接单',
'pending_acceptance' => '待接单',
'pending_pickup' => '待取货',
'rider_arrived' => '骑士到店',
'in_delivery' => '配送中',
'completed' => '已完成',
'dispatch_order' => '指派单',
'return_in_progress' => '妥投异常之物品返回中',
'return_completed' => '妥投异常之物品返回完成',
'return_rider_arrived' => '售后取件单送达门店',
'create_order_failed' => '创建运单失败',
'canceled' => '已取消',
],
'dict_shop_local_delivery_order_log' => [
'store' => '商家',
'rider' => '骑手',
'member' => '会员',
'system' => '系统',
'order_accepted' => '商家已接单',
'order_call' => '商家呼叫配送',
'dispatch_order' => '商家指派骑手',
'rider_accepted' => '骑手已接单',
'rider_arrived' => '骑手已到店',
'rider_picked_up' => '骑手已取货',
'order_delivered' => '订单已送达',
'order_canceled' => '订单已取消',
'return_applied' => '物品返回申请',
'return_completed' => '物品返回完成',
],
'dict_shop_local_delivery_order_cancel' => [
'no_rider' => '没有配送员接单',
'no_rider_pickup' => '配送员没来取货',
'rider_bad_attitude' => '配送员态度太差',
'customer_cancel' => '顾客取消订单',
'order_write_error' => '订单填写错误',
'rider_cancel' => '配送员让我取消此单',
'rider_unwill' => '配送员不愿上门取货',
'rider_unnecessary' => '我不需要配送了',
'rider_cannot_complete' => '配送员以各种理由表示无法完成订单',
'other_reason' => '其他',
],
'dict_shop_coupon' => [
'user' => '手动领取',

View File

@ -0,0 +1,43 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\listener\local_delivery\dada\local_delivery_type;
/**
* 配送服务商-达达秒送
* @package addon\shop\app\listener\local_delivery\dada\local_delivery_type
*/
class LocalDeliveryTypeListener
{
public function handle($params)
{
return [
'dada' => [
'name' => '达达秒送',
'key' => 'dada',
//配置参数
'params' => [
'app_key' => 'APP_KEY',
'app_secret' => 'APP_SECRET',
'source_id' => '商户SourceID',
'shop_no' => '门店编号',
],
'encrypt_params' => ['app_key', 'app_secret', 'source_id', 'shop_no'],
'component' => '/src/addon/shop/views/delivery/components/local-delivery-service-dada.vue',
'business_list' => json_decode(file_get_contents(root_path() . '/addon/shop/app/dict/local_delivery/dada/json/shop_catetory_dict.json'), true),
'extend_data' => [],
//商家配送标识
'is_merchant' => 0
],
];
}
}

View File

@ -0,0 +1,155 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\listener\local_delivery\dada\order;
use addon\shop\app\dict\local_delivery\dada\DadaDeliveryDict;
use addon\shop\app\dict\local_delivery\LocalDeliveryStatusDict;
use addon\shop\app\service\core\local_delivery\CoreLocalDeliveryService;
use addon\shop\core\local_delivery\LocalDeliveryLoader;
use app\model\member\Member;
use core\exception\CommonException;
/**
* 查询配送订单的起始点和终点
* Class GetLocalDeliveryTrack
* @package addon\shop\app\listener\local_delivery\dada\order
*/
class GetLocalDeliveryTrack
{
public function handle($params)
{
$status = $params['status'];
$member_id = $params['member_id'];
$delivery_no = $params['delivery_no'];
$delivery_service = $params['delivery_service'];
if ($delivery_service == DadaDeliveryDict::DADA) {
$member_info = (new Member())->where([['member_id', '=', $member_id]])->findOrEmpty()->toArray();
if (empty($member_info)) throw new CommonException('MEMBER_NOT_EXIST');
$headimg = empty($member_info['headimg']) ? 'static/resource/images/default_headimg.png' : $member_info['headimg'];
if (in_array($status, [LocalDeliveryStatusDict::PENDING_PICKUP, LocalDeliveryStatusDict::IN_DELIVERY])) {
$config = (new CoreLocalDeliveryService())->getConfig($delivery_service);
$loader = new LocalDeliveryLoader($delivery_service, $config);
$data = [
'delivery_no' => $delivery_no,
];
$response_data = $loader->getTransporterPosition($data);
}
switch ($status) {
case LocalDeliveryStatusDict::ORDER_ACCEPTED:
case LocalDeliveryStatusDict::PENDING_ACCEPTANCE:
return [
[
'points_type' => 'shop',
'latitude' => $params['delivery_start_lat'],
'longitude' => $params['delivery_start_lng'],
'icon_path' => 'addon/shop/local_delivery/in_stock.png',
'callout' => [
'content' => '备货中',
],
],
[
'points_type' => 'member',
'latitude' => $params['delivery_end_lat'],
'longitude' => $params['delivery_end_lng'],
'icon_path' => $headimg,
'callout' => [
'content' => '',
],
]
];
case LocalDeliveryStatusDict::PENDING_PICKUP:
if (!empty($response_data['rider_latitude']) && !empty($response_data['rider_longitude'])) {
return [
[
'points_type' => 'rider',
'latitude' => $response_data['rider_latitude'],
'longitude' => $response_data['rider_longitude'],
'icon_path' => 'addon/shop/local_delivery/in_delivery.png',
'callout' => [
'content' => '骑手正赶往商家',
],
],
[
'points_type' => 'shop',
'latitude' => $params['delivery_start_lat'],
'longitude' => $params['delivery_start_lng'],
'icon_path' => 'addon/shop/local_delivery/in_stock.png',
'callout' => [
'content' => '',
],
]
];
} else {
return [
[
'points_type' => 'shop',
'latitude' => $params['delivery_start_lat'],
'longitude' => $params['delivery_start_lng'],
'icon_path' => 'addon/shop/local_delivery/in_stock.png',
'callout' => [
'content' => '骑手正赶往商家',
],
]
];
}
case LocalDeliveryStatusDict::RIDER_ARRIVED:
return [
[
'points_type' => 'rider_arrived',
'latitude' => $params['delivery_start_lat'],
'longitude' => $params['delivery_start_lng'],
'icon_path' => 'addon/shop/local_delivery/picking_up.png',
'callout' => [
'content' => '骑手到店取餐中',
],
],
[
'points_type' => 'member',
'latitude' => $params['delivery_end_lat'],
'longitude' => $params['delivery_end_lng'],
'icon_path' => $headimg,
'callout' => [
'content' => '',
],
]
];
case LocalDeliveryStatusDict::IN_DELIVERY:
return [
[
'points_type' => 'rider',
'latitude' => $response_data['rider_latitude'] ?? $params['delivery_start_lat'],
'longitude' => $response_data['rider_longitude'] ?? $params['delivery_start_lng'],
'icon_path' => 'addon/shop/local_delivery/in_delivery.png',
'callout' => [
'content' => '骑手正在送餐',
],
],
[
'points_type' => 'member',
'latitude' => $params['delivery_end_lat'],
'longitude' => $params['delivery_end_lng'],
'icon_path' => $headimg,
'callout' => [
'content' => '',
],
]
];
default:
return [];
}
}
}
}

View File

@ -0,0 +1,42 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\listener\local_delivery\dada\order;
use addon\shop\app\dict\local_delivery\dada\DadaDeliveryDict;
/**
* 支付方式
* @package addon\mall\app\listener\diy
*/
class OrderCancelReasonListener
{
public function handle($params)
{
// dada 订单取消原因
if (!empty($params['service']) && $params['service'] == DadaDeliveryDict::DADA) {
return [
1 => get_lang('dict_shop_local_delivery_order_cancel.no_rider'), //没有配送员接单,
2 => get_lang('dict_shop_local_delivery_order_cancel.no_rider_pickup'), //配送员没来取货,
3 => get_lang('dict_shop_local_delivery_order_cancel.rider_bad_attitude'), //配送员态度太差,
4 => get_lang('dict_shop_local_delivery_order_cancel.customer_cancel'), //顾客取消订单,
5 => get_lang('dict_shop_local_delivery_order_cancel.order_write_error'), //订单填写错误,
34 => get_lang('dict_shop_local_delivery_order_cancel.rider_cancel'), //配送员让我取消此单,
35 => get_lang('dict_shop_local_delivery_order_cancel.rider_unwill'), //配送员不愿上门取货,
36 => get_lang('dict_shop_local_delivery_order_cancel.rider_unnecessary'), //我不需要配送了,
37 => get_lang('dict_shop_local_delivery_order_cancel.rider_cannot_complete'), //配送员以各种理由表示无法完成订单,
10000 => get_lang('dict_shop_local_delivery_order_cancel.other_reason'), //其他,
];
}
}
}

View File

@ -0,0 +1,51 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\listener\local_delivery\dada\order;
use addon\shop\app\dict\local_delivery\dada\DadaDeliveryDict;
use addon\shop\app\dict\local_delivery\LocalDeliveryStatusDict;
/**
* 配送订单操作前置状态
* @package addon\b2b2c_dada\app\listener\order
*/
class OrderPreStatusListener
{
public function handle($params)
{
if (!empty($params['service']) && $params['service'] == DadaDeliveryDict::DADA) {
return [
'cancel' => [
LocalDeliveryStatusDict::WAIT_RELEASE,
LocalDeliveryStatusDict::PENDING_ACCEPTANCE,
LocalDeliveryStatusDict::PENDING_PICKUP,
LocalDeliveryStatusDict::RIDER_ARRIVED,
], //取消订单
'sync' => [
LocalDeliveryStatusDict::ORDER_ACCEPTED,
LocalDeliveryStatusDict::PENDING_ACCEPTANCE,
LocalDeliveryStatusDict::PENDING_PICKUP,
LocalDeliveryStatusDict::RIDER_ARRIVED,
LocalDeliveryStatusDict::IN_DELIVERY,
LocalDeliveryStatusDict::COMPLETED,
LocalDeliveryStatusDict::DISPATCH_ORDER,
LocalDeliveryStatusDict::RETURN_IN_PROGRESS,
LocalDeliveryStatusDict::RETURN_COMPLETED,
LocalDeliveryStatusDict::RETURN_RIDER_ARRIVED,
LocalDeliveryStatusDict::CREATE_ORDER_FAILED,
LocalDeliveryStatusDict::CANCELED,
], //同步订单
];
}
}
}

View File

@ -18,19 +18,20 @@ class OrderDelivery extends BaseNoticeTemplate
if ($this->key == $params['key']) {
$order = (new CoreOrderService())->getInfo($params['data']['order_id']);
if (!empty($order)) {
$order_data = $params['data'];
$wap_domain = get_wap_domain();
return $this->toReturn(
[
'__wechat_page' => $wap_domain . '/addon/shop/pages/order/detail?order_id=' . $order['order_id'],//模板消息链接
'__weapp_page' => 'addon/shop/pages/order/detail?order_id=' . $order['order_id'],//小程序链接
'body' => str_sub($order['body']),
'order_no' => $order['order_no'],
'delivery_time' => $order['delivery_time'],
'order_money' => $order['order_money'],
'url' => $wap_domain . '/addon/shop/pages/order/detail?order_id=' . $order['order_id']
'__wechat_page' => $wap_domain . '/addon/shop/pages/order/detail?order_id=' . $order_data['order_id'],//模板消息链接
'__weapp_page' => 'addon/shop/pages/order/detail?order_id=' . $order_data['order_id'],//小程序链接
'body' => str_sub($order_data['body']),
'order_no' => $order_data['order_no'],
'delivery_time' => $order_data['delivery_time'],
'order_money' => $order_data['order_money'],
'url' => $wap_domain . '/addon/shop/pages/order/detail?order_id=' . $order_data['order_id']
],
[
'member_id' => $order['member_id']
'member_id' => $order_data['member_id']
]
);
}

View File

@ -32,13 +32,17 @@ class AfterShopOrderClose
$order_discount_where = array(
['order_id', '=', $order_data['order_id']]
);
$order_discount = (new OrderDiscounts())->where($order_discount_where)->select();
if (!$order_discount->isEmpty()) {
$recover_list = [];
foreach ($order_discount as $v) {
$item_discount_type = $v['discount_type'];
$recover_list[$item_discount_type][] = $v['discount_type_id'];
}
foreach ($recover_list as $item_discount_type => $discount_type_ids) {
switch ($item_discount_type) {
case 'coupon'://优惠券
@ -52,6 +56,7 @@ class AfterShopOrderClose
['is_gift', '=', 0],
);
$order_goods_data = (new OrderGoods())->where($order_goods_where)->select()->toArray();
//返还商品库存
$core_goods_stock_service = new CoreGoodsStockService();
$inc_data = [];

View File

@ -36,7 +36,7 @@ class AfterShopOrderDelivery
//消息发送
if ($order_data[ 'status' ] == OrderDict::WAIT_TAKE) {
( new NoticeService() )->send('shop_order_delivery', [ 'order_id' => $order_data[ 'order_id' ] ]);
( new NoticeService() )->send('shop_order_delivery', $order_data);
}
//写入定时收货任务

View File

@ -53,8 +53,8 @@ class AfterShopOrderFinish
}
// 订单完成发放积分成长值
CoreMemberService::sendGrowth($order_data[ 'member_id' ], 'shop_buy_goods', $order_data);
CoreMemberService::sendGrowth($order_data[ 'member_id' ], 'shop_buy_order');
CoreMemberService::sendGrowth($order_data[ 'member_id' ], 'shop_buy_goods', array_merge($order_data, [ 'from_type' => 'shop_buy_goods', 'related_id' => $order_data[ 'order_id' ] ]));
CoreMemberService::sendGrowth($order_data[ 'member_id' ], 'shop_buy_order',['from_type'=>'shop_buy_order','related_id'=>$order_data[ 'order_id' ] ]);
CoreMemberService::sendPoint($order_data[ 'member_id' ], 'shop_buy_goods', array_merge($order_data, [ 'from_type' => 'consume_reward', 'related_id' => $order_data[ 'order_id' ] ]));
// 微信小程序 发送 确认收货提醒消息

View File

@ -14,7 +14,9 @@ namespace addon\shop\app\listener\order_export;
use addon\shop\app\dict\order\InvoiceDict;
use addon\shop\app\model\order\Order;
use addon\shop\app\model\order\OrderGoods;
use app\dict\pay\PayDict;
use think\db\Query;
use think\facade\Db;
/**
* 订单导出数据源查询
@ -31,15 +33,35 @@ class ShopOrderExportDataListener
$model = new Order();
$orderGoodsModel = new OrderGoods();
$pay_where = [];
if ($param[ 'where' ][ 'pay_type' ]) {
$pay_where[] = [ 'pay.type', '=', $param[ 'where' ][ 'pay_type' ] ];
if ($param[ 'where' ]['pay_type']) {
if ($param[ 'where' ]['pay_type'] == PayDict::FRIENDSPAY) {
$pay_where = [
['pay.main_id', '<>', Db::raw("pay.from_main_id")],
['pay.from_main_id', '>', 0],
['pay.status', '=', PayDict::STATUS_FINISH]
];
} else {
$pay_where[] = ['pay.type', '=', $param[ 'where' ]['pay_type']];
}
}
$member_where = [];
if ($param['where']['keyword'] != '') {
$member_where = [
['member.member_no|member.nickname|member.username|member.mobile', 'like', "%" . $param[ 'where' ]['keyword'] . "%"],
];
}
//查询导出订单项数据
$order_ids = $model->where([ [ 'order.order_id', '>', 0 ] ])->withSearch([ 'search_type', 'order_from', 'join_status', 'create_time', 'join_pay_time' ], $param[ 'where' ])
$order_ids = $model->where([ [ 'order.order_id', '>', 0 ] ])
->withSearch([ 'search_type', 'order_from', 'join_status', 'create_time', 'join_pay_time' ], $param[ 'where' ])
->withJoin([
'pay' => function(Query $query) use ($pay_where) {
$query->where($pay_where);
} ], 'left')
},
'member' => function (Query $query) use ($member_where) {
$query->where($member_where);
},
], 'left')
->field('order_id')->order('order.create_time desc')->column('order_id');
$order_goods_field = 'extend,order_goods_id,order_id,member_id,goods_id,sku_id,delivery_id,goods_name,sku_name,goods_image,sku_image,price,num,goods_money,is_enable_refund,goods_type,delivery_status,status';
@ -56,7 +78,6 @@ class ShopOrderExportDataListener
$data = $search_model->select()->toArray();
}
$is_first = true;
$encountered = array();
foreach ($data as $key => $val) {

View File

@ -13,7 +13,9 @@ namespace addon\shop\app\listener\order_export;
use addon\shop\app\model\order\Order;
use addon\shop\app\model\order\OrderGoods;
use app\dict\pay\PayDict;
use think\db\Query;
use think\facade\Db;
/**
* 订单项导出数据源查询
@ -31,16 +33,36 @@ class ShopOrderGoodsExportDataListener
$orderGoodsModel = new OrderGoods();
$order = 'order.create_time desc';
$pay_where = [];
if($param['where'][ 'pay_type' ]){
$pay_where[] = ['pay.type', '=', $param['where'][ 'pay_type' ] ];
if ($param[ 'where' ]['pay_type']) {
if ($param[ 'where' ]['pay_type'] == PayDict::FRIENDSPAY) {
$pay_where = [
['pay.main_id', '<>', Db::raw("pay.from_main_id")],
['pay.from_main_id', '>', 0],
['pay.status', '=', PayDict::STATUS_FINISH]
];
} else {
$pay_where[] = ['pay.type', '=', $param[ 'where' ]['pay_type']];
}
}
$member_where = [];
if ($param['where']['keyword'] != '') {
$member_where = [
['member.member_no|member.nickname|member.username|member.mobile', 'like', "%" . $param[ 'where' ]['keyword'] . "%"],
];
}
//查询导出订单项数据
$order_ids = $model->where([[ 'order.order_id', '>', 0 ]])->withSearch([ 'search_type', 'order_from', 'join_status', 'create_time', 'join_pay_time' ], $param['where'])
$order_ids = $model->where([ [ 'order.order_id', '>', 0 ] ])
->withSearch([ 'search_type', 'order_from', 'join_status', 'create_time', 'join_pay_time' ], $param[ 'where' ])
->withJoin([
'pay' => function(Query $query) use($pay_where){
'pay' => function(Query $query) use ($pay_where) {
$query->where($pay_where);
}], 'left')
->field('order_id')->order($order)->column('order_id');
},
'member' => function (Query $query) use ($member_where) {
$query->where($member_where);
},
], 'left')
->field('order_id')->order('order.create_time desc')->column('order_id');
$order_goods_field = 'order_goods_id,order_id,member_id,delivery_id,goods_name,sku_name,price,num,goods_money,discount_money,order_refund_no,order_goods_money,goods_type,delivery_status,status';
$order_field = 'order_id,order_no,order_from,taker_name,taker_mobile,taker_full_address,create_time,pay_time,delivery_money,delivery_type,delivery_time,finish_time,member_remark,shop_remark,out_trade_no,status';

View File

@ -60,8 +60,9 @@ class AfterShopOrderClose
'total_point_num' => Db::raw('total_point_num-' . $order_data['point']),
'total_price_num' => Db::raw('total_price_num-' . $v['goods_money']),
'total_order_num' => Db::raw('total_order_num-1'),
'goods_id' => $v['goods_id'],
'sku_id' => $v['sku_id'],
'update_time'=>time(),
// 'goods_id' => $v['goods_id'],
// 'sku_id' => $v['sku_id'],
'id' => $order_data['relate_id'],
];

View File

@ -58,8 +58,9 @@ class AfterShopOrderCreate
'total_point_num' => Db::raw('total_point_num+' . $order_data['point']),
'total_price_num' => Db::raw('total_price_num+' . $v['goods_money']),
'total_order_num' => Db::raw('total_order_num+1'),
'goods_id' => $v['goods_id'],
'sku_id' => $v['sku_id'],
'update_time' => time(),
// 'goods_id' => $v['goods_id'],
// 'sku_id' => $v['sku_id'],
'id' => $order_data['relate_id'],
];
}

View File

@ -114,7 +114,8 @@ class ShopPoster
])->findOrEmpty()->toArray();
if (!empty($newcomer_goods_info) && $newcomer_goods_info[ 'active' ][ 'active_status' ] == ActiveDict::ACTIVE) {
$url_data[] = [ 'key' => 'type', 'value' => 'newcomer_discount' ];
// $url_data[] = [ 'key' => 'type', 'value' => 'newcomer_discount' ];
$url_data[] = [ 'key' => 'type', 'value' => 'nd' ];
$newcomer_price = json_decode($newcomer_goods_info[ 'active_goods_value' ], true)[ 'newcomer_price' ];
if ($newcomer_price > 0) $show_price_data[] = $newcomer_price;
}

View File

@ -35,7 +35,7 @@ class AfterShopOrderRefundFinish
if ($order[ 'status' ] == OrderDict::WAIT_DELIVERY) {
//校验一下订单项是否全部发货
( new CoreOrderDeliveryService() )->checkFinish(
[ 'order_id' => $refund_data[ 'order_id' ]]
[ 'order_id' => $refund_data[ 'order_id' ] ]
);
}
//校验一下是否全部退款
@ -58,7 +58,8 @@ class AfterShopOrderRefundFinish
'main_type' => $main_type,
'main_id' => $main_id,
'type' => OrderRefundDict::FINISH_ACTION,
'content' => ''
'content' => '',
'create_time'=>time()+1
]);
//消息发送

View File

@ -47,7 +47,7 @@ class ShopOrderRefundExportDataListener
$data = $search_model->select()->toArray();
}
foreach ($data as $key => $val) {
$data[$key]['nickname'] = $val['member']['nickname'];
$data[$key]['nickname'] = $val['member']['nickname'] ?? "";
}
foreach ($data as $key => $val) {
$data[$key]['order_refund_no'] = $val['order_refund_no']."\t";

View File

@ -0,0 +1,37 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\shop\app\listener\shop_delivery;
use addon\shop\app\dict\local_delivery\LocalDeliveryStatusDict;
use addon\shop\app\dict\shop_delivery\ShopDeliveryDict;
/**
* 配送订单操作前置状态
* @package addon\shop\app\listener\shop_delivery
*/
class OrderPreStatusListener
{
public function handle($params)
{
if (!empty($params['service']) && $params['service'] == ShopDeliveryDict::MERCHANT) {
return [
'finish' => [
LocalDeliveryStatusDict::IN_DELIVERY
], //完成订单
'cancel' => [
LocalDeliveryStatusDict::IN_DELIVERY
], //取消订单
];
}
}
}

View File

@ -31,11 +31,6 @@ class OrderAmountServiceListener
// 'name' => '配送费',
// 'key' => 'delivery_money',
// 'amount' => 20
// ],
// [
// 'name' => '服务费',
// 'key' => 'service_money',
// 'amount' => 10
// ]
// ];
$this->getDeliveryMoney($data);

Some files were not shown because too many files have changed in this diff Show More